Reproducing Krishnan, Heer, Cherian & Sheffield (2022) from dandiset 000462¶

Paper. Krishnan S, Heer C, Cherian C, Sheffield MEJ (2022). Reward expectation extinction restructures and degrades CA1 spatial maps through loss of a dopaminergic reward proximity signal. Nature Communications 13, 6662. doi:10.1038/s41467-022-34465-5

Dataset. DANDI:000462, version 0.230416.2132 (doi:10.48324/dandi.000462/0.230416.2132), streamed. The paper cites no version of the dandiset. This is the only published version; the draft (modified 2023-06-20) has the same 14 assets and size and is not fixed, so the published version is pinned.

Claims. claims/000462/10.1038-s41467-022-34465-5.yaml, written from the version of record (PMC9636178) with its Supplementary Information, and checked by a second reader.

What the dandiset holds. One file per mouse: 10 wild-type mice of the reward extinction experiment (5 with R, UR, RR and a novel environment; 5 with R and UR only), and 4 DAT-Cre hM4D(Gi) mice with day 1 (R, UR, RR) and days 2 to 4 (before and after saline, CNO and DCZ). Each file holds raw suite2p fluorescence and three behaviour channels at 30 Hz. It does not hold the time-control mice, the dark sessions, the pupil video, the tdTomato control mice or the VTA axon imaging, and it has 10 of the 12 wild-type mice and 4 of the 6 DREADD mice.

Claim Tested here
session-lap-counts, lick-persistence-ur tested
fig1d, fig1e, fig1f, fig1g, supp4-lickstop-definition-robust, supp4-lapwise-r2, supp5-not-running-speed tested (decoder)
approach-ratio (R and UR; the dark sessions are not in the dandiset), engaged-lap-counts, supp6b-decoder-engaged-laps, disengaged-laps-uniform tested
fig2a, fig2c, fig2d, fig2e, fig3a, fig3b (three claims), fig3c, supp11, fig3d, reward-zone-field-turnover tested (place fields)
supp13-novel-environment tested
supp16, fig4f, supp18, fig4h, fig4m tested (DREADD mice)
supp2-decoder-stable-control, controls-no-change-over-time blocked: the control mice are not in the dandiset
supp6-pupil-engagement blocked: no pupil data
supp15-tdtomato-controls blocked: the tdTomato mice are not in the dandiset
fig5-da-axon-ramps, fig5k-ramp-slope-max, fig5l-ramps-return-rr, supp20-novel-axons, supp21-heterogeneous-axons blocked: no axon imaging
supp14-reward-cells blocked: the reward cell criteria are in ref. 24, not in the paper

Revision after the first verification round: the approach ratio that defines engaged laps is now taken over the traversal of the track, leaving out the pause after the reward, and laps with an undefined ratio are neither engaged nor disengaged; the reward zone starts at the first lick before the reward; a check of the clock against the 1.5 s reward pause, the contents of every file and the pooled-percentile saline split were added. Every analysis that uses engaged laps was rerun with the new split.

The analysis of every mouse (dF/F, transients, decoders, place fields) runs once, in the next cells, with the helper krishnan.py; each claim's section then reads the results. Where each parameter comes from is in the helper's docstring.

In [1]:
import os
os.environ.setdefault("OMP_NUM_THREADS", "1")
import time
import warnings

import numpy as np
import pandas as pd
from scipy import stats

import krishnan as K

warnings.filterwarnings("ignore", category=RuntimeWarning)
pd.set_option("display.width", 250)
pd.set_option("display.max_columns", 40)
WT = K.WT_FULL + K.WT_RUR
DR = K.DREADD
# approach ratios: the main reading defines engaged laps; the others are variants
RATIO_LABELS = {"ratios_trav": "Methods ratio, traversal only (main)", "ratios": "Methods ratio, whole lap",
                "ratios_code_trav": "code ratio, traversal only", "ratios_code": "code ratio, whole lap"}
ENG_KEYS = ["ratios_trav", "ratios", "ratios_code_trav"]
print("dandiset", K.DANDISET, "version", K.VERSION)

def mean_ci(x):
    """Mean and half-width of the t 95% confidence interval."""
    x = np.asarray(x, float)
    x = x[np.isfinite(x)]
    if len(x) < 2:
        return (float(x.mean()) if len(x) else np.nan, np.nan)
    return float(x.mean()), float(stats.t.ppf(0.975, len(x) - 1) * x.std(ddof=1) / np.sqrt(len(x)))
dandiset 000462 version 0.230416.2132
In [2]:
t0 = time.time()
RES = K.process_all(WT + DR, workers=2)
print(f"processed {len(RES)} mice in {(time.time() - t0) / 60:.1f} min")
processed 14 mice in 8.2 min

Data check¶

In [3]:
assets = K.assets()
print("assets:", len(assets), "| subjects:", sorted(assets))
rows = []
for s, r in RES.items():
    rows.append({"mouse": s, "description": r["subject_description"][:40], "cells": r["n_cells"], "fluorescence frames": r["f_shape"][0],
                 "behaviour rows in file": r["behaviour_rows_in_file"], "F min": round(r["F_min"]), "F median": round(r["F_median"]),
                 "offset removed": round(r["offset"]), "position V": tuple(round(v, 3) for v in r["volts"]["position_V"]),
                 "200 cm at V": round(r["scale"], 3), "reward V": tuple(round(v, 2) for v in r["volts"]["reward_V"]),
                 "lick V": tuple(round(v, 2) for v in r["volts"]["lick_V"]),
                 "reward between 0.5-4 V": round(r["volts"]["reward_frac_between_0.5_and_4V"], 4),
                 "lick between 0.5-4 V": round(r["volts"]["lick_frac_between_0.5_and_4V"], 4),
                 "behaviour md5": r["fingerprints"]["behaviour_md5"], "fluorescence md5": r["fingerprints"]["fluorescence_md5"]})
info = pd.DataFrame(rows).set_index("mouse")
print(info.to_string())
print("distinct behaviour fingerprints:", info["behaviour md5"].nunique(), "| distinct fluorescence fingerprints:", info["fluorescence md5"].nunique(), "of", len(info))
cond_rows = []
for s, r in RES.items():
    for n, c in r["beh"]["conds"].items():
        cond_rows.append({"mouse": s, "condition": n, "frames": c["n_frames"], "complete laps": len(K.complete_laps(c)),
                          "lap segments": len(c["laps"]), "rewards": len(c["rewards"]), "lick onsets": len(c["licks"]),
                          "transient frac": round(r["transient_frac"][n], 3), "dF/F 1/50/99 pct": tuple(round(v, 3) for v in r["dff_pct"][n])})
cdf = pd.DataFrame(cond_rows)
print(cdf.to_string(index=False))
print("speed range (cm/s) over running frames:", round(min(np.percentile(c["speed"][c["speed"] >= 1], 1) for r in RES.values() for c in r["beh"]["conds"].values()), 1),
      "to", round(max(np.percentile(c["speed"], 99.9) for r in RES.values() for c in r["beh"]["conds"].values()), 1))
assets: 14 | subjects: ['009', '011', '012', '014', 'CFC16', 'CFC17', 'CFC19', 'CFC4', 'NR14', 'NR21', 'NR23', 'NR32', 'NR34', 'NR6']
                                    description  cells  fluorescence frames  behaviour rows in file  F min  F median  offset removed       position V  200 cm at V       reward V         lick V  reward between 0.5-4 V  lick between 0.5-4 V behaviour md5 fluorescence md5
mouse                                                                                                                                                                                                                                                                        
NR6                              C57BL/6J mouse    343                70000                   70008    564      1916               0   (0.005, 0.613)        0.612  (-0.02, 9.02)   (-0.02, 5.2)                     0.0                0.0030  42d21f88447f     d32b954ffffa
NR14                             C57BL/6J mouse    456                70000                   70000    678      2893               0   (0.005, 0.615)        0.614  (-0.01, 9.03)   (-0.02, 5.2)                     0.0                0.0037  f5f6654d58a9     11102300c302
NR21                             C57BL/6J mouse    576                65000                   65000    396      1620               0  (-0.002, 0.613)        0.610  (-0.02, 9.02)  (-0.02, 5.23)                     0.0                0.0027  38410f6f804a     22d24446dda5
NR23                             C57BL/6J mouse    860                65000                   65000    352      1561               0   (0.001, 0.613)        0.612  (-0.02, 9.01)  (-0.03, 5.23)                     0.0                0.0032  30506586ac8e     18b20e43a5fb
CFC4                             C57BL/6J mouse   1924                65000                   64999    188      1850               0   (0.004, 0.613)        0.611  (-0.02, 9.02)  (-0.04, 5.23)                     0.0                0.0010  735cbb18f9c6     cdba6d377fe0
NR32                             C57BL/6J mouse    690                40000                   40000    277      2501               0   (0.002, 0.613)        0.612  (-0.03, 9.01)  (-0.07, 5.26)                     0.0                0.0004  c2c58f5d1524     00b1242f088e
NR34                             C57BL/6J mouse    982                40000                   40000    330      2488               0   (0.003, 0.612)        0.611  (-0.02, 9.01)  (-0.08, 5.26)                     0.0                0.0007  fb04e320c9c9     adc9db303707
CFC16                            C57BL/6J mouse   1443                35000                   35000     57      1559               0   (0.009, 0.615)        0.615   (0.01, 9.04)   (-0.02, 5.2)                     0.0                0.0017  de1fd970a6e6     e23a52d35fd6
CFC17                            C57BL/6J mouse    418                35000                   35000    452      2354               0   (0.009, 0.616)        0.615    (0.0, 9.04)  (-0.02, 5.21)                     0.0                0.0027  4e3087f6292b     bc1ed3edac15
CFC19                            C57BL/6J mouse    968                35000                   35000    162      1614               0   (0.009, 0.616)        0.615    (0.0, 9.04)   (-0.02, 5.2)                     0.0                0.0022  ba9f17e6c27f     45760fd26e0d
009    Dat-Cre mice injected with inhibitory DR   1218               135000                  135000  16471     17467           16471   (0.001, 0.617)        0.614  (-0.02, 9.03)  (-0.09, 5.24)                     0.0                0.0008  d5c3d86e1c8c     0ce4a9c58f3c
011    Dat-Cre mice injected with inhibitory DR   1635               135000                  135000  16443     17405           16443   (0.001, 0.617)        0.615  (-0.02, 9.03)  (-0.07, 5.22)                     0.0                0.0012  f03abaee9a4c     3de538931f82
012    Dat-Cre mice injected with inhibitory DR   1958               143000                  143000  16415     17555           16415   (0.001, 0.616)        0.614  (-0.02, 9.02)  (-0.08, 5.22)                     0.0                0.0006  2793dba064bd     cbe18c0c0050
014    Dat-Cre mice injected with inhibitory DR   1239               135000                  129000  16465     17290           16465   (0.002, 0.617)        0.615  (-0.02, 9.03)  (-0.07, 5.23)                     0.0                0.0013  3e996723511e     67564e1abdc8
distinct behaviour fingerprints: 14 | distinct fluorescence fingerprints: 14 of 14
mouse    condition  frames  complete laps  lap segments  rewards  lick onsets  transient frac       dF/F 1/50/99 pct
  NR6     Rewarded   20000             43            44       43         1088           0.076 (-0.041, 0.066, 0.812)
  NR6   Unrewarded   20000             32            33        1           91           0.064 (-0.042, 0.069, 0.767)
  NR6   ReRewarded   15000             25            26       24          753           0.061 (-0.046, 0.074, 0.727)
  NR6        Novel   15000             33            35       34          798           0.069 (-0.049, 0.076, 0.969)
 NR14     Rewarded   15000             30            32       30          715           0.056  (-0.04, 0.064, 0.519)
 NR14   Unrewarded   20000             23            24        1          111           0.050 (-0.039, 0.063, 0.548)
 NR14   ReRewarded   20000             20            22       20          500           0.048 (-0.042, 0.067, 0.496)
 NR14        Novel   15000             20            21       20          315           0.061 (-0.043, 0.069, 0.746)
 NR21     Rewarded   15000             40            40       40         1035           0.104 (-0.041, 0.072, 0.708)
 NR21   Unrewarded   20000             32            33        0           31           0.096   (-0.04, 0.07, 0.817)
 NR21   ReRewarded   15000             22            24       22          708           0.091 (-0.043, 0.074, 0.716)
 NR21        Novel   15000             20            22       21          781           0.102 (-0.042, 0.075, 0.988)
 NR23     Rewarded   15000             40            42       40          764           0.037   (-0.06, 0.094, 0.54)
 NR23   Unrewarded   20000             32            34        1           64           0.037  (-0.06, 0.096, 0.601)
 NR23   ReRewarded   15000             37            38       37          856           0.038 (-0.064, 0.102, 0.614)
 NR23        Novel   15000             50            51       50          875           0.041 (-0.066, 0.105, 0.704)
 CFC4     Rewarded   15000             39            40       38         1596           0.042 (-0.071, 0.112, 0.755)
 CFC4   Unrewarded   20000             61            63        0          145           0.050 (-0.071, 0.115, 0.987)
 CFC4   ReRewarded   15000             37            38       37         1639           0.037 (-0.073, 0.117, 0.652)
 CFC4        Novel   14999             41            42       41         1354           0.046 (-0.075, 0.121, 0.912)
 NR32     Rewarded   15000             28            30       28          518           0.065  (-0.056, 0.092, 0.75)
 NR32   Unrewarded   25000             21            22        0           23           0.061 (-0.058, 0.094, 0.751)
 NR34     Rewarded   15000             30            32       31          646           0.065 (-0.058, 0.096, 0.799)
 NR34   Unrewarded   25000             17            18        0           78           0.061 (-0.059, 0.098, 0.784)
CFC16     Rewarded   15000             31            32       31         1172           0.032 (-0.112, 0.177, 1.021)
CFC16   Unrewarded   20000             26            28        1           96           0.036 (-0.113, 0.181, 1.157)
CFC17     Rewarded   15000             36            38       36         1191           0.080  (-0.045, 0.07, 0.835)
CFC17   Unrewarded   20000             36            36        0           54           0.075 (-0.045, 0.071, 0.829)
CFC19     Rewarded   15000             36            38       36         1166           0.060 (-0.066, 0.106, 0.754)
CFC19   Unrewarded   20000             17            17        0          117           0.054 (-0.065, 0.107, 0.749)
  009     Rewarded   12000             28            29       28          419           0.041  (-0.073, 0.116, 0.74)
  009   Unrewarded   15000             19            20        0          131           0.042 (-0.071, 0.112, 0.784)
  009   ReRewarded   12000             21            23       21          451           0.042 (-0.073, 0.116, 0.799)
  009 BeforeSaline   12000             29            30       29          431           0.042  (-0.077, 0.12, 0.807)
  009  AfterSaline   20000             44            45       44          648           0.041 (-0.079, 0.124, 0.837)
  009    BeforeCNO   12000             24            25       24         1028           0.041 (-0.079, 0.125, 0.799)
  009     AfterCNO   20000             25            26       25          799           0.037 (-0.078, 0.124, 0.783)
  009    BeforeDCZ   12000             23            25       24         1053           0.040 (-0.081, 0.126, 0.853)
  009     AfterDCZ   20000             25            26       25         1331           0.036  (-0.08, 0.125, 0.759)
  011     Rewarded   12000             54            55       54         1141           0.029  (-0.153, 0.25, 1.362)
  011   Unrewarded   15000             24            25        0          126           0.019  (-0.154, 0.25, 1.257)
  011   ReRewarded   12000             38            40       38         1060           0.025  (-0.163, 0.265, 1.36)
  011 BeforeSaline   12000             44            45       45         1037           0.029 (-0.156, 0.254, 1.333)
  011  AfterSaline   20000             65            67       65         1516           0.028 (-0.163, 0.264, 1.394)
  011    BeforeCNO   12000             49            50       49          980           0.027   (-0.16, 0.264, 1.36)
  011     AfterCNO   20000             51            52       51         1493           0.027  (-0.161, 0.263, 1.33)
  011    BeforeDCZ   12000             46            47       46         1243           0.036  (-0.15, 0.245, 1.355)
  011     AfterDCZ   20000             51            52       51         2197           0.029 (-0.156, 0.252, 1.301)
  012     Rewarded   12000             40            42       41         1259           0.043 (-0.087, 0.138, 0.933)
  012   Unrewarded   15000             29            30        0          195           0.038 (-0.087, 0.137, 0.857)
  012 BeforeSaline   12000             37            38       37          612           0.041 (-0.088, 0.139, 0.864)
  012  AfterSaline   20000             68            69       68          541           0.038  (-0.089, 0.14, 0.796)
  012    BeforeCNO   12000             34            36       34          473           0.038 (-0.088, 0.139, 0.791)
  012     AfterCNO   20000             31            32       31          268           0.033  (-0.089, 0.14, 0.779)
  012    BeforeDCZ   12000             32            33       32         1407           0.042 (-0.084, 0.134, 0.819)
  012     AfterDCZ   40000             45            45       45         1562           0.033 (-0.085, 0.134, 0.763)
  014     Rewarded   12000             54            56       54         1443           0.071  (-0.07, 0.112, 1.292)
  014   Unrewarded   15000             46            47        0           65           0.092   (-0.07, 0.112, 1.81)
  014   ReRewarded   12000             41            43       41         1099           0.081 (-0.071, 0.114, 1.628)
  014 BeforeSaline   12000             47            48       47         1466           0.078  (-0.07, 0.113, 1.351)
  014  AfterSaline   20000             91            92       92         2128           0.077  (-0.07, 0.114, 1.338)
  014    BeforeCNO   12000             29            31       30          534           0.074  (-0.08, 0.128, 1.515)
  014     AfterCNO   20000             38            39       38         1365           0.075   (-0.075, 0.12, 1.54)
  014    BeforeDCZ   12000             51            52       51         1640           0.072 (-0.079, 0.122, 1.315)
  014     AfterDCZ   20000             27            28       27          898           0.069 (-0.073, 0.117, 1.206)
speed range (cm/s) over running frames: 1.0 to 119.5
In [4]:
# Clock and timing check against the paper's 1.5 s pause after each reward (Methods, Behavior and virtual reality),
# and the laps per minute that the lap counts and the condition lengths imply (independent of how a lap time is defined)
trows = []
for s, r in RES.items():
    for n, c in r["beh"]["conds"].items():
        tm = r["beh"]["timing"][n]
        if not len(tm["reward_to_teleport"]):
            continue
        lt = r["beh"]["lap_times"][n]
        run_t = np.array([(c["speed"][a:e] >= K.RUN_SPEED).sum() / K.FS for a, e in K.complete_laps(c)])
        trows.append({"mouse": s, "condition": n, "rewarded laps": len(tm["reward_to_teleport"]),
                      "frames end->reward (median)": float(np.median(tm["top_to_reward"])),
                      "frames reward->teleport (median)": float(np.median(tm["reward_to_teleport"])),
                      "reward->teleport s at 30 Hz": round(float(np.median(tm["reward_to_teleport"])) / K.FS, 2),
                      "inter-reward s (median)": round(float(np.median(tm["inter_reward_s"])), 2),
                      "laps per min": round(len(K.complete_laps(c)) / (c["n_frames"] / K.FS / 60), 2),
                      "lap time 0-200 cm s (mean)": round(float(np.nanmean(lt)), 2),
                      "running time per lap s (mean)": round(float(np.mean(run_t)), 2)})
tdf = pd.DataFrame(trows)
print(tdf.to_string(index=False))
print("over all rewarded conditions: reward->teleport median", np.median(tdf["frames reward->teleport (median)"]), "frames, range",
      tdf["frames reward->teleport (median)"].min(), "to", tdf["frames reward->teleport (median)"].max())
wt_r = tdf[(tdf["mouse"].isin(WT)) & (tdf["condition"] == "Rewarded")]
print(f"wild-type R: laps per minute {wt_r['laps per min'].mean():.2f} (range {wt_r['laps per min'].min()} to {wt_r['laps per min'].max()}), "
      f"lap time {wt_r['lap time 0-200 cm s (mean)'].mean():.2f} s, inter-reward interval {wt_r['inter-reward s (median)'].mean():.2f} s")
# Full subject descriptions, and every object in every file (to look for pupil, axon or other data)
for s, r in RES.items():
    print(s, "|", r["subject_description"])
objs = {}
for s, a in assets.items():
    f = K.open_file(a["asset_id"])
    names = []
    f.visititems(lambda n, o: names.append(n + (f" {o.shape} {o.dtype}" if hasattr(o, "shape") else "/")))
    objs[s] = names
    f.close()
common = set.intersection(*[set(n.split(" ")[0] for n in v) for v in objs.values()])
print("HDF5 objects present in every file:", sorted(common))
for s, v in objs.items():
    extra = [n for n in v if n.split(" ")[0] not in common]
    print(s, "objects not in every file:", extra if extra else "none", "| datasets:", [n for n in v if " (" in n and n.startswith(("acquisition", "processing", "analysis", "stimulus"))])
mouse    condition  rewarded laps  frames end->reward (median)  frames reward->teleport (median)  reward->teleport s at 30 Hz  inter-reward s (median)  laps per min  lap time 0-200 cm s (mean)  running time per lap s (mean)
  NR6     Rewarded             43                          3.0                              48.0                         1.60                    15.18          3.87                       13.73                          10.71
  NR6   Unrewarded              1                          9.0                              49.0                         1.63                      NaN          2.88                       19.09                          12.87
  NR6   ReRewarded             24                          4.0                              48.0                         1.60                    16.97          3.00                       17.61                          11.41
  NR6        Novel             33                          4.0                              48.0                         1.60                    14.47          3.96                       13.25                          12.36
 NR14     Rewarded             30                          3.0                              48.0                         1.60                    16.07          3.60                       14.66                           9.11
 NR14   Unrewarded              1                          2.0                              48.0                         1.60                      NaN          2.07                       27.08                          12.78
 NR14   ReRewarded             20                          3.0                              48.0                         1.60                    22.63          1.80                       30.26                          15.41
 NR14        Novel             20                          2.5                              47.0                         1.57                    21.77          2.40                       23.00                          15.33
 NR21     Rewarded             40                          2.0                              48.0                         1.60                    11.73          4.80                        7.52                           8.67
 NR21   ReRewarded             22                          4.0                              47.0                         1.57                    15.80          2.64                       13.44                          13.23
 NR21        Novel             20                          2.0                              47.0                         1.57                    19.83          2.40                       16.33                          15.46
 NR23     Rewarded             40                          2.0                              47.0                         1.57                    11.27          4.80                       10.25                           7.89
 NR23   ReRewarded             37                          1.0                              47.0                         1.57                    12.48          4.44                       10.62                           9.13
 NR23        Novel             50                          0.0                              47.0                         1.57                     9.63          6.00                        8.06                           7.45
 CFC4     Rewarded             38                          2.0                              47.0                         1.57                    11.23          4.68                       10.99                           5.98
 CFC4   ReRewarded             37                          2.0                              47.0                         1.57                    12.20          4.44                       11.83                           7.62
 CFC4        Novel             41                          2.0                              46.0                         1.53                    11.58          4.92                       10.51                           8.19
 NR32     Rewarded             28                          1.0                              48.0                         1.60                    17.30          3.36                       15.32                          10.65
 NR34     Rewarded             30                          2.0                              47.0                         1.57                    14.45          3.60                       14.15                          10.33
CFC16     Rewarded             31                          4.0                              48.0                         1.60                    14.27          3.72                       14.08                           6.56
CFC17     Rewarded             36                          4.0                              48.0                         1.60                    12.80          4.32                       11.85                           8.34
CFC19     Rewarded             36                          3.0                              48.0                         1.60                    13.57          4.32                       11.93                           7.20
  009     Rewarded             28                          1.0                              48.0                         1.60                    13.47          4.20                       12.09                           9.45
  009   ReRewarded             21                          1.0                              48.0                         1.60                    15.97          3.15                       16.96                          12.88
  009 BeforeSaline             29                         -3.0                              48.0                         1.60                    12.67          4.35                       12.16                           8.87
  009  AfterSaline             44                          1.0                              48.0                         1.60                    14.93          3.96                       13.10                           9.75
  009    BeforeCNO             24                          1.0                              48.0                         1.60                    15.77          3.60                       14.38                          10.31
  009     AfterCNO             25                          2.0                              48.0                         1.60                    21.75          2.25                       21.41                          15.27
  009    BeforeDCZ             23                         -1.5                              48.0                         1.60                    15.87          3.45                       14.10                           9.71
  009     AfterDCZ             25                          2.0                              48.0                         1.60                    24.45          2.25                       23.05                          14.15
  011     Rewarded             54                          1.0                              48.0                         1.60                     7.40          8.10                        5.78                           4.96
  011   ReRewarded             38                         -5.5                              48.0                         1.60                     8.80          5.70                        8.53                           6.22
  011 BeforeSaline             44                          1.0                              48.0                         1.60                     9.02          6.60                        7.31                           5.63
  011  AfterSaline             65                          1.0                              48.0                         1.60                     9.92          5.85                        8.36                           6.25
  011    BeforeCNO             49                         -5.0                              48.0                         1.60                     7.92          7.35                        6.69                           5.34
  011     AfterCNO             51                        -12.5                              48.0                         1.60                    12.95          4.59                       11.36                           7.56
  011    BeforeDCZ             46                          0.0                              48.0                         1.60                     8.50          6.90                        6.65                           5.69
  011     AfterDCZ             51                         -7.0                              47.0                         1.57                    12.52          4.59                       10.35                           7.42
  012     Rewarded             40                          1.0                              48.0                         1.60                     8.08          6.00                        7.36                           6.84
  012 BeforeSaline             37                          1.0                              48.0                         1.60                    10.25          5.55                        8.95                           7.11
  012  AfterSaline             68                          1.0                              48.0                         1.60                     9.50          6.12                        8.13                           7.21
  012    BeforeCNO             34                         -1.0                              48.0                         1.60                    10.80          5.10                       10.06                           7.93
  012     AfterCNO             31                         -2.0                              48.0                         1.60                    17.37          2.79                       18.80                          14.17
  012    BeforeDCZ             32                         -6.0                              48.0                         1.60                    11.77          4.80                       10.23                           7.74
  012     AfterDCZ             45                          1.0                              48.0                         1.60                    18.05          2.02                       27.85                          17.40
  014     Rewarded             54                          1.0                              48.0                         1.60                     6.83          8.10                        5.74                           5.06
  014   ReRewarded             41                         -6.5                              48.0                         1.60                     8.90          6.15                        7.93                           6.95
  014 BeforeSaline             47                          1.0                              48.0                         1.60                     7.82          7.05                        6.83                           5.71
  014  AfterSaline             91                        -11.5                              48.0                         1.60                     6.93          8.19                        6.01                           4.89
  014    BeforeCNO             29                          1.0                              48.0                         1.60                     6.50          4.35                        5.07                           4.35
  014     AfterCNO             38                          1.0                              48.0                         1.60                    15.87          3.42                       14.97                          10.91
  014    BeforeDCZ             51                          0.0                              48.0                         1.60                     7.40          7.65                        6.23                           5.23
  014     AfterDCZ             27                          0.0                              48.0                         1.60                    21.18          2.43                       20.99                          13.41
over all rewarded conditions: reward->teleport median 48.0 frames, range 46.0 to 49.0
wild-type R: laps per minute 4.11 (range 3.36 to 4.8), lap time 12.45 s, inter-reward interval 13.79 s
NR6 | C57BL/6J mouse
NR14 | C57BL/6J mouse
NR21 | C57BL/6J mouse
NR23 | C57BL/6J mouse
CFC4 | C57BL/6J mouse
NR32 | C57BL/6J mouse
NR34 | C57BL/6J mouse
CFC16 | C57BL/6J mouse
CFC17 | C57BL/6J mouse
CFC19 | C57BL/6J mouse
009 | Dat-Cre mice injected with inhibitory DREADD receptor in VTA-Dopmaine neurons
011 | Dat-Cre mice injected with inhibitory DREADD receptor in VTA-Dopmaine neurons
012 | Dat-Cre mice injected with inhibitory DREADD receptor in VTA-Dopmaine neurons
014 | Dat-Cre mice injected with inhibitory DREADD receptor in VTA-Dopmaine neurons
HDF5 objects present in every file: ['acquisition/', 'acquisition/Behaviordata/', 'acquisition/Behaviordata/data', 'acquisition/Behaviordata/starting_time', 'acquisition/Cellxrawfluoresence/', 'acquisition/Cellxrawfluoresence/data', 'acquisition/Cellxrawfluoresence/starting_time', 'analysis/', 'file_create_date', 'general/', 'general/devices/', 'general/devices/Rig1/', 'general/experiment_description', 'general/experimenter', 'general/institution', 'general/keywords', 'general/lab', 'general/optophysiology/', 'general/optophysiology/ImagingPlane/', 'general/optophysiology/ImagingPlane/OpticalChannel/', 'general/optophysiology/ImagingPlane/OpticalChannel/description', 'general/optophysiology/ImagingPlane/OpticalChannel/emission_lambda', 'general/optophysiology/ImagingPlane/description', 'general/optophysiology/ImagingPlane/excitation_lambda', 'general/optophysiology/ImagingPlane/imaging_rate', 'general/optophysiology/ImagingPlane/indicator', 'general/optophysiology/ImagingPlane/location', 'general/related_publications', 'general/subject/', 'general/subject/age', 'general/subject/description', 'general/subject/sex', 'general/subject/species', 'general/subject/subject_id', 'identifier', 'processing/', 'processing/ophys/', 'session_description', 'session_start_time', 'specifications/', 'specifications/core/', 'specifications/core/2.6.0-alpha/', 'specifications/core/2.6.0-alpha/namespace', 'specifications/core/2.6.0-alpha/nwb.base', 'specifications/core/2.6.0-alpha/nwb.behavior', 'specifications/core/2.6.0-alpha/nwb.device', 'specifications/core/2.6.0-alpha/nwb.ecephys', 'specifications/core/2.6.0-alpha/nwb.epoch', 'specifications/core/2.6.0-alpha/nwb.file', 'specifications/core/2.6.0-alpha/nwb.icephys', 'specifications/core/2.6.0-alpha/nwb.image', 'specifications/core/2.6.0-alpha/nwb.misc', 'specifications/core/2.6.0-alpha/nwb.ogen', 'specifications/core/2.6.0-alpha/nwb.ophys', 'specifications/core/2.6.0-alpha/nwb.retinotopy', 'specifications/hdmf-common/', 'specifications/hdmf-common/1.5.1/', 'specifications/hdmf-common/1.5.1/base', 'specifications/hdmf-common/1.5.1/namespace', 'specifications/hdmf-common/1.5.1/sparse', 'specifications/hdmf-common/1.5.1/table', 'specifications/hdmf-experimental/', 'specifications/hdmf-experimental/0.2.0/', 'specifications/hdmf-experimental/0.2.0/experimental', 'specifications/hdmf-experimental/0.2.0/namespace', 'specifications/hdmf-experimental/0.2.0/resources', 'stimulus/', 'stimulus/presentation/', 'stimulus/templates/', 'timestamps_reference_time']
NR32 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (40000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (40000, 690) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
NR34 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (40000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (40000, 982) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
NR6 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (70008, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (70000, 343) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
CFC17 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (35000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (35000, 418) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
CFC19 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (35000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (35000, 968) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
CFC16 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (35000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (35000, 1443) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
NR23 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (65000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (65000, 860) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
NR14 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (70000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (70000, 456) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
NR21 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (65000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (65000, 576) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
CFC4 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (64999, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (65000, 1924) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
009 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (135000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (135000, 1218) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
014 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (129000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (135000, 1239) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
012 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (143000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (143000, 1958) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']
011 objects not in every file: none | datasets: ['acquisition/Behaviordata/data (135000, 3) float64', 'acquisition/Behaviordata/starting_time () float64', 'acquisition/Cellxrawfluoresence/data (135000, 1635) float64', 'acquisition/Cellxrawfluoresence/starting_time () float64']

The timing check and the file contents:

  • In every rewarded condition of every file the teleport follows the reward onset by a median of 46 to 49 frames, 1.53 to 1.63 s at 30 Hz, against the 1.5 s pause of the Methods (Behavior and virtual reality), and the reward comes a median of 0 to 4 frames after the position first reaches the track end in the wild-type mice. The behaviour clock is therefore consistent with the stated 30 Hz.
  • The wild-type mice run 3.36 to 4.8 R laps per minute (mean 4.11) from the lap counts and condition lengths alone, close to the training criterion of more than 4 laps per minute (Methods). Their mean R lap time from 0 to 200 cm is 12.45 s (running frames only, 8.5 s on average and 6.0 to 10.7 s per mouse). The paper's 7.34 s per matched R lap is shorter than both means, though the running time of 3 mice (CFC4 5.98 s, CFC16 6.56 s, CFC19 7.20 s) is below it. Lap time is independent of the volts-to-cm scale, and the clock agrees with the reward pause, so the slower lap times here do not come from the clock or the scale; they come from the data or from a definition of lap time the paper does not give. The lap speeds of supp16 and supp18 are 200 cm over the same lap times.
  • Every file holds the same HDF5 objects: the two acquisition series (behaviour, frames x 3, and raw fluorescence, frames x cells), their starting times, and metadata under general/. processing/ophys/ and analysis/ are empty. No file holds pupil, axon imaging or any other data.
  • The subject descriptions, printed in full above, are "C57BL/6J mouse" for the 10 wild-type files and "Dat-Cre mice injected with inhibitory DREADD receptor in VTA-Dopmaine neurons" for the 4 DREADD files; none describes a tdTomato control.

The checks against the description:

  • Frame counts: the description of each fluorescence series lists the conditions and their frame counts; they sum to the fluorescence frames in every file. The behaviour rows match the fluorescence frames in 11 files; sub-NR6 has 8 extra behaviour rows at the end, sub-CFC4 one row fewer, and sub-014 6000 rows fewer (cell after next).
  • Conditions: rewards are delivered in R, RR, the novel session and all DREADD sessions, and not in UR, consistent with the condition order in the description, except that 4 mice (NR6, NR14, NR23, CFC16) receive one reward in UR (the rewards column above). In NR6 and NR14 it falls at the end of the first complete UR lap (timing table, next cell); in NR23 and CFC16 it falls in the partial lap that opens UR, which no analysis uses. Either the boundary taken from the frame counts is a little early or the reward was switched off a lap late. The lap is kept in UR, as the frame counts say; a variant in lick-persistence-ur counts it as R.
  • Units: the position channel is in volts (0 to about 0.61 V) with no stated scale; the reward is delivered at the top of the range, so the top is read as 200 cm (assumption). The reward and lick channels are two-level signals (almost no samples between 0.5 and 4 V).
  • Fluorescence: the wild-type files have a minimum near 0 counts; the DREADD files have a minimum near 17000 counts with transients a few thousand counts above it, an offset the description does not mention. It is removed (file minimum) before dF/F.
  • The 14 files are distinct: no two share the behaviour or fluorescence fingerprints.
  • The 30 Hz rate is the series' stated rate; the description gives no units for position, reward or licks. The next cell checks the rate against the pause after the reward.
In [5]:
# Clock check: decoding R position with the true labels against labels shifted by 10 s
for s in WT:
    d = RES[s]["dec_shift"]
    print(f"{s}: held-out R2 with aligned labels {d[0]:.3f}, with labels shifted 10 s {d[300]:.3f}")
# Locating the missing behaviour frames of sub-014
gap = pd.DataFrame(K.gap_check("014"))
print(gap.round(1).to_string(index=False))
NR6: held-out R2 with aligned labels 0.739, with labels shifted 10 s 0.227
NR14: held-out R2 with aligned labels 0.701, with labels shifted 10 s -0.241
NR21: held-out R2 with aligned labels 0.745, with labels shifted 10 s -0.125
NR23: held-out R2 with aligned labels 0.793, with labels shifted 10 s 0.089
CFC4: held-out R2 with aligned labels 0.956, with labels shifted 10 s 0.229
NR32: held-out R2 with aligned labels 0.884, with labels shifted 10 s -0.520
NR34: held-out R2 with aligned labels 0.799, with labels shifted 10 s -0.177
CFC16: held-out R2 with aligned labels 0.913, with labels shifted 10 s -1.284
CFC17: held-out R2 with aligned labels 0.938, with labels shifted 10 s -0.890
CFC19: held-out R2 with aligned labels 0.945, with labels shifted 10 s 0.430
behaviour frames  error, fluorescence lag 0 (cm)  error, fluorescence lag 6000 (cm)
     65000-66000                            13.6                               25.0
     66000-67000                             9.8                               25.3
     67000-68000                             6.8                               22.9
     68000-69000                             9.2                               27.0
     69000-70000                             8.7                               21.4
     70000-71000                            13.4                               27.2
     71000-72000                            22.9                               19.2
     72000-73000                            18.7                               10.2
     73000-74000                            22.3                               10.6
     74000-75000                            23.8                               10.3
     75000-76000                            21.4                               11.0
     76000-77000                            21.2                               11.2
     77000-78000                            42.7                               40.3
     78000-79000                            36.0                               25.5
     79000-80000                            25.5                               10.5
     80000-81000                            28.9                                9.4
     81000-82000                            37.5                                9.8
     82000-83000                            30.9                                6.4

In every wild-type mouse the position decodes from the fluorescence with aligned labels (R2 above 0.7) and not with labels shifted by 10 s, so behaviour and fluorescence share a clock at the scale of the decoder. In sub-014 the fluorescence after frame 71000 matches the behaviour 6000 frames earlier; before it, it matches with no shift (table above, a model trained on the aligned BeforeSaline condition). The 6000 missing behaviour rows are therefore at the start of BeforeCNO; the helper inserts them as missing (BEHAVIOUR_GAPS), and laps that touch them are dropped. For the DREADD mice the clock is shown by the place fields found in every condition (next sections); no decoder was run on them.

In [6]:
# File metadata named in the findings, and the effect of the DREADD offset on dF/F (first 9000 frames, 40 cells)
mrows = []
for s, a in assets.items():
    f = K.open_file(a["asset_id"])
    ip = f["general/optophysiology/ImagingPlane"]
    row = {"mouse": s, "session_start_time": f["session_start_time"][()].decode(), "age": f["general/subject/age"][()].decode(),
           "indicator": ip["indicator"][()].decode(), "imaging plane description": ip["description"][()].decode(),
           "session description": f["session_description"][()].decode()}
    if RES[s]["offset"] > 0:
        F = f["acquisition/Cellxrawfluoresence/data"][:9000, :40].astype(np.float32)
        p_rem = np.percentile(K.dff(np.maximum(F - RES[s]["offset"], 1)), 99)
        p_raw = np.percentile(K.dff(F), 99)
        row["dF/F 99th pct offset removed / kept"] = f"{p_rem:.2f} / {p_raw:.3f} (ratio {p_rem / p_raw:.1f})"
    f.close()
    mrows.append(row)
print(pd.DataFrame(mrows).set_index("mouse").to_string())
              session_start_time   age indicator            imaging plane description            session description dF/F 99th pct offset removed / kept
mouse                                                                                                                                                   
NR32   2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
NR34   2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
NR6    2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
CFC17  2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
CFC19  2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
CFC16  2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
NR23   2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
NR14   2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
NR21   2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
CFC4   2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction                                 NaN
009    2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction            1.99 / 0.224 (ratio 8.9)
014    2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction           4.05 / 0.378 (ratio 10.7)
012    2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction            2.82 / 0.353 (ratio 8.0)
011    2021-12-13T02:30:00-06:00  P12W       GFP  calcium imaging of CA1 with GCaMP6f  Reward expectation extinction           2.83 / 0.234 (ratio 12.1)

The file metadata: every file gives the ImagingPlane indicator as 'GFP', while the plane's own description says GCaMP6f; every file has the same session_start_time (2021-12-13T02:30:00-06:00) and age (P12W); and every file, the 4 DREADD files with four days of sessions included, has the session description 'Reward expectation extinction'. In the first 9000 frames of 40 cells of each DREADD file, the 99th percentile of dF/F is 1.99 to 4.05 with the offset removed and 0.22 to 0.38 with it kept, 8.0 to 12.1 times smaller.

session-lap-counts¶

Mice ran on average 34 laps in R, 28 in UR and 27 in RR (mean +/- 95% CI 34 +/- 2, 28 +/- 4 and 27 +/- 3), and 31 +/- 5 laps in the novel rewarded environment.

Complete laps (start in the first 10 cm, reach the last 5 cm) and all lap segments, which include a partial lap at the start or end of a condition. The code variant drops the frames the paper's code drops (task_framestokeep).

In [7]:
def code_kept_laps(s, cond_i, c):
    keep = K.CODE_FRAMES_TO_KEEP[s][cond_i]
    stop = keep if keep > 0 else c["n_frames"] + keep
    return sum(1 for a, e, ok in c["laps"] if ok and e <= stop)

rows = []
for s in WT:
    conds = RES[s]["beh"]["conds"]
    row = {"mouse": s}
    for i, (n, c) in enumerate(conds.items()):
        row[n] = len(K.complete_laps(c))
        row[n + " (with partial)"] = len(c["laps"])
        row[n + " (code frames)"] = code_kept_laps(s, i, c)
    rows.append(row)
laps_df = pd.DataFrame(rows).set_index("mouse")
print(laps_df.T.to_string())
expect = {"Rewarded": (34, 2), "Unrewarded": (28, 4), "ReRewarded": (27, 3), "Novel": (31, 5)}
lapcount_measured = {}
for n, (v, tol) in expect.items():
    for suffix in ["", " (with partial)", " (code frames)"]:
        m, h = mean_ci(laps_df[n + suffix].dropna())
        ok = abs(m - v) <= tol
        if suffix == "":
            lapcount_measured[n] = m
        print(f"{n + suffix:32s} mean {m:5.1f} +/- {h:4.1f} (n = {laps_df[n + suffix].notna().sum()}); paper {v} +/- {tol}; within: {ok}")
mouse                       NR6  NR14  NR21  NR23  CFC4  NR32  NR34  CFC16  CFC17  CFC19
Rewarded                   43.0  30.0  40.0  40.0  39.0  28.0  30.0   31.0   36.0   36.0
Rewarded (with partial)    44.0  32.0  40.0  42.0  40.0  30.0  32.0   32.0   38.0   38.0
Rewarded (code frames)     24.0  30.0  39.0  40.0  38.0  28.0  30.0   31.0   36.0   36.0
Unrewarded                 32.0  23.0  32.0  32.0  61.0  21.0  17.0   26.0   36.0   17.0
Unrewarded (with partial)  33.0  24.0  33.0  34.0  63.0  22.0  18.0   28.0   36.0   17.0
Unrewarded (code frames)   28.0  23.0  32.0  32.0  61.0  21.0  17.0   26.0   35.0   16.0
ReRewarded                 25.0  20.0  22.0  37.0  37.0   NaN   NaN    NaN    NaN    NaN
ReRewarded (with partial)  26.0  22.0  24.0  38.0  38.0   NaN   NaN    NaN    NaN    NaN
ReRewarded (code frames)   24.0  20.0  22.0  36.0  36.0   NaN   NaN    NaN    NaN    NaN
Novel                      33.0  20.0  20.0  50.0  41.0   NaN   NaN    NaN    NaN    NaN
Novel (with partial)       35.0  21.0  22.0  51.0  42.0   NaN   NaN    NaN    NaN    NaN
Novel (code frames)        33.0  20.0  20.0  50.0  41.0   NaN   NaN    NaN    NaN    NaN
Rewarded                         mean  35.3 +/-  3.7 (n = 10); paper 34 +/- 2; within: True
Rewarded (with partial)          mean  36.8 +/-  3.5 (n = 10); paper 34 +/- 2; within: False
Rewarded (code frames)           mean  33.2 +/-  3.8 (n = 10); paper 34 +/- 2; within: True
Unrewarded                       mean  29.7 +/-  9.2 (n = 10); paper 28 +/- 4; within: True
Unrewarded (with partial)        mean  30.8 +/-  9.4 (n = 10); paper 28 +/- 4; within: True
Unrewarded (code frames)         mean  29.1 +/-  9.2 (n = 10); paper 28 +/- 4; within: True
ReRewarded                       mean  28.2 +/- 10.2 (n = 5); paper 27 +/- 3; within: True
ReRewarded (with partial)        mean  29.6 +/-  9.7 (n = 5); paper 27 +/- 3; within: True
ReRewarded (code frames)         mean  27.6 +/-  9.7 (n = 5); paper 27 +/- 3; within: True
Novel                            mean  32.8 +/- 16.3 (n = 5); paper 31 +/- 5; within: True
Novel (with partial)             mean  34.2 +/- 16.0 (n = 5); paper 31 +/- 5; within: True
Novel (code frames)              mean  32.8 +/- 16.3 (n = 5); paper 31 +/- 5; within: True

Result: reproduced. Counting complete laps, the means are 35.3 (R, 10 mice), 29.7 (UR), 28.2 (RR, 5 mice) and 32.8 (novel, 5 mice), all within the paper's 34 +/- 2, 28 +/- 4, 27 +/- 3 and 31 +/- 5. Counting partial laps puts R at 36.8, outside its tolerance; the code's frame cut gives 33.2.

lick-persistence-ur¶

After reward removal mice kept pre-licking for about 5 laps (5 +/- 1) before stopping; 4 of 12 mice licked randomly in later laps.

In [8]:
print("reward zone start per mouse (cm, mean position of the first R lick after 20 cm and before the reward; main):",
      {s: round(RES[s]["beh"]["zone"], 1) for s in WT})
print("variant, first lick after 20 cm whenever it falls (first version of this attempt):", {s: round(RES[s]["beh"]["zone_any_lick"], 1) for s in WT})
print("R laps with a lick before the reward (after 20 cm), per mouse:",
      {s: f"{len(RES[s]['beh']['first_lick_positions'])} of {len(K.complete_laps(RES[s]['beh']['conds']['Rewarded']))}" for s in WT})
print("first pre-reward lick position per R lap, 10th/50th/90th percentile (cm):",
      {s: tuple(np.round(np.percentile(RES[s]["beh"]["first_lick_positions"], [10, 50, 90]), 1)) for s in WT})
print("code variant zone start (cm):", {s: RES[s]["beh"]["zone_code"] for s in WT})
lick_rows = []
for s in WT:
    b = RES[s]["beh"]
    z = b["ur_zone_licks"]
    anyl = b["ur_any_licks"]
    first_nolick = int(np.argmax(z == 0)) if (z == 0).any() else len(z)
    row = {"mouse": s, "UR laps": len(z), "zone licks per UR lap": " ".join(map(str, z))}
    for L in K.LICK_READINGS:
        ls = b["lick_stop"][L]
        row[f"RE high laps {L}"] = ls
        row[f"(a) zone lick in RE low {L}"] = bool((z[ls:] > 0).any())
        row[f"(b) any lick in RE low {L}"] = bool((anyl[ls:] > 0).any())
    row["(c) zone lick after first no-lick lap"] = bool((z[first_nolick + 1:] > 0).any())
    row["first no-lick lap"] = first_nolick
    cU = b["conds"]["Unrewarded"]
    s0, e0 = K.complete_laps(cU)[0]
    row["reward in first UR lap"] = bool(((cU["rewards"] >= s0) & (cU["rewards"] < e0)).any())
    lick_rows.append(row)
lick_df = pd.DataFrame(lick_rows).set_index("mouse")
print(lick_df.drop(columns="zone licks per UR lap").to_string())
for s in WT:
    print(s, "zone licks per UR lap:", lick_df.loc[s, "zone licks per UR lap"])
lick_persist = {}
for L in K.LICK_READINGS:
    m, h = mean_ci(lick_df[f"RE high laps {L}"])
    lick_persist[L] = m
    print(f"{L}: RE high laps per mouse mean {m:.2f} +/- {h:.2f} (95% CI, n = {len(WT)}); paper 5 +/- 1; within: {abs(m - 5) <= 1}")
    print(f"    random lickers: (a) {lick_df[f'(a) zone lick in RE low {L}'].sum()}, (b) {lick_df[f'(b) any lick in RE low {L}'].sum()} of {len(WT)} mice")
print(f"    (c) {lick_df['(c) zone lick after first no-lick lap'].sum()} of {len(WT)} mice")
fn = lick_df["first no-lick lap"]
print(f"reading not in the criterion, index of the first UR lap with no zone lick (laps before it): mean {fn.mean():.2f} +/- {mean_ci(fn)[1]:.2f}, per mouse {fn.tolist()}")
rw1 = lick_df["reward in first UR lap"]
print("mice with a reward in the first UR lap:", rw1[rw1].index.tolist())
for L in K.LICK_READINGS:
    adj = lick_df[f"RE high laps {L}"] - rw1.astype(int)
    print(f"variant, first UR lap counted as R where it was rewarded, {L}: RE high laps mean {adj.mean():.2f} +/- {mean_ci(adj)[1]:.2f}")
# code variant of the zone
for L in K.LICK_READINGS:
    v = [K.lick_stop(RES[s]["beh"]["ur_zone_licks_code"] > 0, 2, L) for s in WT]
    print(f"code zone variant {L}: RE high laps mean {np.mean(v):.2f}, per mouse {v}")
# lick stops under the first version's zone (first lick after 20 cm whenever it falls)
for L in K.LICK_READINGS:
    old = {s: K.lick_stop(K.licks_in_zone(RES[s]["beh"]["conds"]["Unrewarded"], RES[s]["beh"]["zone_any_lick"]) > 0, 2, L) for s in WT}
    same = all(old[s] == RES[s]["beh"]["lick_stop"][L] for s in WT)
    print(f"lick stop {L} under the first version's zone, per mouse {old}; same as under the main zone in every mouse: {same}")
reward zone start per mouse (cm, mean position of the first R lick after 20 cm and before the reward; main): {'NR6': 193.5, 'NR14': 167.7, 'NR21': 195.9, 'NR23': 192.5, 'CFC4': 192.2, 'NR32': 193.7, 'NR34': 191.6, 'CFC16': 184.5, 'CFC17': 129.0, 'CFC19': 196.9}
variant, first lick after 20 cm whenever it falls (first version of this attempt): {'NR6': 193.8, 'NR14': 169.8, 'NR21': 197.1, 'NR23': 194.7, 'CFC4': 193.3, 'NR32': 195.1, 'NR34': 192.3, 'CFC16': 185.0, 'CFC17': 132.9, 'CFC19': 197.5}
R laps with a lick before the reward (after 20 cm), per mouse: {'NR6': '41 of 43', 'NR14': '28 of 30', 'NR21': '26 of 40', 'NR23': '27 of 40', 'CFC4': '33 of 39', 'NR32': '21 of 28', 'NR34': '27 of 30', 'CFC16': '30 of 31', 'CFC17': '34 of 36', 'CFC19': '28 of 36'}
first pre-reward lick position per R lap, 10th/50th/90th percentile (cm): {'NR6': (np.float64(188.0), np.float64(193.4), np.float64(198.1)), 'NR14': (np.float64(40.4), np.float64(191.8), np.float64(197.8)), 'NR21': (np.float64(192.4), np.float64(196.3), np.float64(198.7)), 'NR23': (np.float64(186.1), np.float64(197.4), np.float64(198.9)), 'CFC4': (np.float64(187.0), np.float64(194.2), np.float64(198.6)), 'NR32': (np.float64(188.5), np.float64(193.2), np.float64(198.2)), 'NR34': (np.float64(187.6), np.float64(191.9), np.float64(195.0)), 'CFC16': (np.float64(180.2), np.float64(190.5), np.float64(195.7)), 'CFC17': (np.float64(23.6), np.float64(189.1), np.float64(197.6)), 'CFC19': (np.float64(193.3), np.float64(196.9), np.float64(199.7))}
code variant zone start (cm): {'NR6': 0.0, 'NR14': 0.0, 'NR21': 0.0, 'NR23': 0.0, 'CFC4': 0.0, 'NR32': 0.0, 'NR34': 0.0, 'CFC16': 0.0, 'CFC17': 0.0, 'CFC19': 0.0}
       UR laps  RE high laps L1  (a) zone lick in RE low L1  (b) any lick in RE low L1  RE high laps L2  (a) zone lick in RE low L2  (b) any lick in RE low L2  RE high laps L3  (a) zone lick in RE low L3  (b) any lick in RE low L3  (c) zone lick after first no-lick lap  first no-lick lap  reward in first UR lap
mouse                                                                                                                                                                                                                                                                                                                   
NR6         32               12                       False                      False               13                       False                      False               14                       False                      False                                   True                 10                    True
NR14        23               10                       False                      False               11                       False                      False               12                       False                      False                                   True                  7                    True
NR21        32                5                       False                      False                6                       False                      False                7                       False                      False                                   True                  1                   False
NR23        32                2                        True                       True                3                        True                       True                4                        True                       True                                   True                  2                   False
CFC4        61                8                        True                       True                9                        True                       True               10                        True                       True                                   True                  6                   False
NR32        21                3                       False                       True                4                       False                       True                5                       False                       True                                  False                  3                   False
NR34        17               11                       False                       True               12                       False                       True               13                       False                       True                                   True                  6                   False
CFC16       26                2                        True                       True                3                        True                       True                4                        True                       True                                   True                  2                   False
CFC17       36                6                        True                       True                7                        True                       True                8                        True                       True                                   True                  6                   False
CFC19       17                7                       False                       True                8                       False                       True                9                       False                       True                                   True                  5                   False
NR6 zone licks per UR lap: 20 15 11 4 2 1 5 3 3 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
NR14 zone licks per UR lap: 5 15 10 9 7 1 6 0 1 11 0 0 0 0 0 0 0 0 0 0 0 0 0
NR21 zone licks per UR lap: 6 0 5 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
NR23 zone licks per UR lap: 13 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 2 3 0 0 0 0 0 0 0 0
CFC4 zone licks per UR lap: 14 12 7 2 1 2 0 2 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
NR32 zone licks per UR lap: 4 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
NR34 zone licks per UR lap: 13 12 11 7 5 3 0 1 1 0 2 0 0 0 0 0 0
CFC16 zone licks per UR lap: 15 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
CFC17 zone licks per UR lap: 10 8 6 5 1 3 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
CFC19 zone licks per UR lap: 13 7 6 5 3 0 1 0 0 0 0 0 0 0 0 0 0
L1: RE high laps per mouse mean 6.60 +/- 2.62 (95% CI, n = 10); paper 5 +/- 1; within: False
    random lickers: (a) 4, (b) 7 of 10 mice
L2: RE high laps per mouse mean 7.60 +/- 2.62 (95% CI, n = 10); paper 5 +/- 1; within: False
    random lickers: (a) 4, (b) 7 of 10 mice
L3: RE high laps per mouse mean 8.60 +/- 2.62 (95% CI, n = 10); paper 5 +/- 1; within: False
    random lickers: (a) 4, (b) 7 of 10 mice
    (c) 9 of 10 mice
reading not in the criterion, index of the first UR lap with no zone lick (laps before it): mean 4.80 +/- 1.99, per mouse [10, 7, 1, 2, 6, 3, 6, 2, 6, 5]
mice with a reward in the first UR lap: ['NR6', 'NR14']
variant, first UR lap counted as R where it was rewarded, L1: RE high laps mean 6.40 +/- 2.44
variant, first UR lap counted as R where it was rewarded, L2: RE high laps mean 7.40 +/- 2.44
variant, first UR lap counted as R where it was rewarded, L3: RE high laps mean 8.40 +/- 2.44
code zone variant L1: RE high laps mean 6.80, per mouse [12, 10, 5, 2, 8, 4, 11, 3, 6, 7]
code zone variant L2: RE high laps mean 7.80, per mouse [13, 11, 6, 3, 9, 5, 12, 4, 7, 8]
code zone variant L3: RE high laps mean 8.80, per mouse [14, 12, 7, 4, 10, 6, 13, 5, 8, 9]
lick stop L1 under the first version's zone, per mouse {'NR6': 12, 'NR14': 10, 'NR21': 5, 'NR23': 2, 'CFC4': 8, 'NR32': 3, 'NR34': 11, 'CFC16': 2, 'CFC17': 6, 'CFC19': 7}; same as under the main zone in every mouse: True
lick stop L2 under the first version's zone, per mouse {'NR6': 13, 'NR14': 11, 'NR21': 6, 'NR23': 3, 'CFC4': 9, 'NR32': 4, 'NR34': 12, 'CFC16': 3, 'CFC17': 7, 'CFC19': 8}; same as under the main zone in every mouse: True
lick stop L3 under the first version's zone, per mouse {'NR6': 14, 'NR14': 12, 'NR21': 7, 'NR23': 4, 'CFC4': 10, 'NR32': 5, 'NR34': 13, 'CFC16': 4, 'CFC17': 8, 'CFC19': 9}; same as under the main zone in every mouse: True

Result: not reproduced. The reward zone now starts at the mean position of the first lick before the reward (184.5 to 196.9 cm in 8 mice, 167.7 in NR14 and 129.0 in CFC17); counting any first lick moved it by 0.3 to 3.9 cm and changed no lick stop under L1, L2 or L3 (last lines of the output). Mice kept licking in the reward zone for several UR laps, but the mean number of RE high laps is 6.60 +/- 2.62 under L1 (7.60 under L2, 8.60 under L3), outside 5 +/- 1 under every reading, and 6.40 to 8.40 when the rewarded first UR lap of NR6 and NR14 is counted as R. Random licking after the lick stop by reading (a), a zone lick in an RE low lap, is found in 4 of the 10 mice, the paper's count; reading (b) gives 7 and reading (c) 9. The index of the first UR lap with no zone lick, a reading the criterion does not list, averages 4.80 +/- 1.99 laps; it is shown, not judged, and is asked of a reader (questions file).

fig1f-decoder-r2¶

Mean decoder R2 is 0.95 in R, 0.90 in RE high laps (similar to R), 0.65 in RE low laps (significantly lower), and 0.82 in RR, which recovers somewhat but stays below R.

Decoder: Gaussian naive Bayes on the dF/F of all cells, running frames, 40 bins, trained on the first 60% of R laps, the track treated as a circle (each prediction taken at its nearest copy of the true bin), R2 per lap between true and predicted bin (Methods, Position decoding, as the criterion gives it). The same decoder without the circular treatment is shown for comparison. Per mouse (main reading, from the code's plot_shuffleerror_withlickstop): R is the mean over random draws of 4 of the last 5 R laps, RE high and RE low of draws of min(4, lick stop) and min(3, lick stop) laps from those laps, RR of min(4, lick stop) RR laps. The mean over many such draws converges to the mean over the laps drawn from, which is computed exactly here (last 5 R laps, all RE high, all RE low, all RR laps). The variant is the code's decoder (first 50% of R frames, unwrapped).

In [9]:
def lap_r2(s, cond, key="dec"):
    return {d["lap"]: d["r2"] for d in RES[s][key][cond]}

def per_mouse_r2(s, L, key="dec", reading="main"):
    b = RES[s]["beh"]
    ls = b["lick_stop"][L]
    r = lap_r2(s, "Rewarded", key)
    u = lap_r2(s, "Unrewarded", key)
    out = {}
    rl = [r[k] for k in sorted(r)]
    out["R"] = np.nanmean(rl[-5:]) if reading == "main" else np.nanmean(rl)
    out["RE high"] = np.nanmean([v for k, v in u.items() if k < ls]) if any(k < ls for k in u) else np.nan
    out["RE low"] = np.nanmean([v for k, v in u.items() if k >= ls]) if any(k >= ls for k in u) else np.nan
    if "ReRewarded" in RES[s][key]:
        out["RR"] = np.nanmean(list(lap_r2(s, "ReRewarded", key).values()))
    else:
        out["RR"] = np.nan
    return out

def paired(a, b):
    a, b = np.asarray(a, float), np.asarray(b, float)
    ok = np.isfinite(a) & np.isfinite(b)
    return stats.ttest_rel(a[ok], b[ok]), ok.sum()

fig1f = {}
for key, label in [("dec", "Methods decoder (60% of R laps, circular)"), ("dec_var", "code decoder (50% of R frames)"), ("dec_lin", "60% of R laps, not circular")]:
    for reading in ["main", "all tested R laps"]:
        for L in K.LICK_READINGS:
            df = pd.DataFrame({s: per_mouse_r2(s, L, key, reading) for s in WT}).T
            means = {c: mean_ci(df[c].dropna()) for c in df.columns}
            tests = {}
            for a, b2 in [("R", "RE high"), ("R", "RE low"), ("R", "RR"), ("RE high", "RE low"), ("RE high", "RR"), ("RE low", "RR")]:
                t, n = paired(df[a], df[b2])
                tests[(a, b2)] = (t.statistic, t.pvalue, n)
            fig1f[(key, reading, L)] = (df, means, tests)
            print(f"--- {label}, R laps {reading}, lick stop {L}")
            print("  ", "; ".join(f"{c} {m:.3f} [{m - h:.3f} {m + h:.3f}] (n={df[c].notna().sum()})" for c, (m, h) in means.items()))
            print("  ", "; ".join(f"{a}-{b2}: t={t:.2f} p={p:.4f} (x6 Bonferroni {min(1, 6 * p):.4f}, n={n})" for (a, b2), (t, p, n) in tests.items()))
df, means, tests = fig1f[("dec", "main", "L1")]
print("\nper mouse, Methods decoder, main reading, L1:\n", df.round(3).to_string())
fig1f_ok = {}
for k, (df, means, tests) in fig1f.items():
    if k[0] == "dec_lin":
        continue
    within = all(abs(means[c][0] - v) <= tol for c, (v, tol) in {"R": (0.95, 0.02), "RE high": (0.90, 0.03), "RE low": (0.65, 0.11), "RR": (0.82, 0.05)}.items())
    for corr in [1, 6]:
        sig = (tests[("R", "RE low")][1] * corr < 0.05, tests[("R", "RR")][1] * corr < 0.05, tests[("R", "RE high")][1] * corr >= 0.05)
        fig1f_ok[k + (corr,)] = (within, sig)
        print(k, "Bonferroni x6" if corr == 6 else "uncorrected", "means within tolerance:", within, "| R<RE low, R<RR significant, R vs RE high not:", sig)
--- Methods decoder (60% of R laps, circular), R laps main, lick stop L1
   R 0.960 [0.926 0.994] (n=10); RE high 0.901 [0.863 0.939] (n=10); RE low 0.606 [0.442 0.769] (n=10); RR 0.861 [0.771 0.951] (n=5)
   R-RE high: t=3.73 p=0.0047 (x6 Bonferroni 0.0281, n=10); R-RE low: t=4.94 p=0.0008 (x6 Bonferroni 0.0048, n=10); R-RR: t=3.15 p=0.0345 (x6 Bonferroni 0.2068, n=5); RE high-RE low: t=4.19 p=0.0023 (x6 Bonferroni 0.0140, n=10); RE high-RR: t=2.14 p=0.0988 (x6 Bonferroni 0.5930, n=5); RE low-RR: t=-4.23 p=0.0133 (x6 Bonferroni 0.0799, n=5)
--- Methods decoder (60% of R laps, circular), R laps main, lick stop L2
   R 0.960 [0.926 0.994] (n=10); RE high 0.888 [0.843 0.934] (n=10); RE low 0.584 [0.390 0.777] (n=10); RR 0.861 [0.771 0.951] (n=5)
   R-RE high: t=5.64 p=0.0003 (x6 Bonferroni 0.0019, n=10); R-RE low: t=4.39 p=0.0017 (x6 Bonferroni 0.0104, n=10); R-RR: t=3.15 p=0.0345 (x6 Bonferroni 0.2068, n=5); RE high-RE low: t=3.42 p=0.0076 (x6 Bonferroni 0.0457, n=10); RE high-RR: t=0.63 p=0.5609 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.97 p=0.0166 (x6 Bonferroni 0.0995, n=5)
--- Methods decoder (60% of R laps, circular), R laps main, lick stop L3
   R 0.960 [0.926 0.994] (n=10); RE high 0.868 [0.811 0.925] (n=10); RE low 0.564 [0.345 0.784] (n=10); RR 0.861 [0.771 0.951] (n=5)
   R-RE high: t=6.11 p=0.0002 (x6 Bonferroni 0.0011, n=10); R-RE low: t=4.04 p=0.0029 (x6 Bonferroni 0.0176, n=10); R-RR: t=3.15 p=0.0345 (x6 Bonferroni 0.2068, n=5); RE high-RE low: t=2.98 p=0.0156 (x6 Bonferroni 0.0934, n=10); RE high-RR: t=-0.78 p=0.4766 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.53 p=0.0242 (x6 Bonferroni 0.1451, n=5)
--- Methods decoder (60% of R laps, circular), R laps all tested R laps, lick stop L1
   R 0.977 [0.965 0.990] (n=10); RE high 0.901 [0.863 0.939] (n=10); RE low 0.606 [0.442 0.769] (n=10); RR 0.861 [0.771 0.951] (n=5)
   R-RE high: t=5.49 p=0.0004 (x6 Bonferroni 0.0023, n=10); R-RE low: t=5.31 p=0.0005 (x6 Bonferroni 0.0029, n=10); R-RR: t=4.62 p=0.0099 (x6 Bonferroni 0.0592, n=5); RE high-RE low: t=4.19 p=0.0023 (x6 Bonferroni 0.0140, n=10); RE high-RR: t=2.14 p=0.0988 (x6 Bonferroni 0.5930, n=5); RE low-RR: t=-4.23 p=0.0133 (x6 Bonferroni 0.0799, n=5)
--- Methods decoder (60% of R laps, circular), R laps all tested R laps, lick stop L2
   R 0.977 [0.965 0.990] (n=10); RE high 0.888 [0.843 0.934] (n=10); RE low 0.584 [0.390 0.777] (n=10); RR 0.861 [0.771 0.951] (n=5)
   R-RE high: t=5.37 p=0.0005 (x6 Bonferroni 0.0027, n=10); R-RE low: t=4.73 p=0.0011 (x6 Bonferroni 0.0065, n=10); R-RR: t=4.62 p=0.0099 (x6 Bonferroni 0.0592, n=5); RE high-RE low: t=3.42 p=0.0076 (x6 Bonferroni 0.0457, n=10); RE high-RR: t=0.63 p=0.5609 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.97 p=0.0166 (x6 Bonferroni 0.0995, n=5)
--- Methods decoder (60% of R laps, circular), R laps all tested R laps, lick stop L3
   R 0.977 [0.965 0.990] (n=10); RE high 0.868 [0.811 0.925] (n=10); RE low 0.564 [0.345 0.784] (n=10); RR 0.861 [0.771 0.951] (n=5)
   R-RE high: t=5.07 p=0.0007 (x6 Bonferroni 0.0040, n=10); R-RE low: t=4.35 p=0.0019 (x6 Bonferroni 0.0112, n=10); R-RR: t=4.62 p=0.0099 (x6 Bonferroni 0.0592, n=5); RE high-RE low: t=2.98 p=0.0156 (x6 Bonferroni 0.0934, n=10); RE high-RR: t=-0.78 p=0.4766 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.53 p=0.0242 (x6 Bonferroni 0.1451, n=5)
--- code decoder (50% of R frames), R laps main, lick stop L1
   R 0.954 [0.919 0.988] (n=10); RE high 0.899 [0.857 0.941] (n=10); RE low 0.601 [0.435 0.768] (n=10); RR 0.851 [0.750 0.951] (n=5)
   R-RE high: t=3.44 p=0.0073 (x6 Bonferroni 0.0440, n=10); R-RE low: t=4.78 p=0.0010 (x6 Bonferroni 0.0060, n=10); R-RR: t=3.21 p=0.0325 (x6 Bonferroni 0.1948, n=5); RE high-RE low: t=4.27 p=0.0021 (x6 Bonferroni 0.0125, n=10); RE high-RR: t=2.00 p=0.1160 (x6 Bonferroni 0.6963, n=5); RE low-RR: t=-4.40 p=0.0117 (x6 Bonferroni 0.0701, n=5)
--- code decoder (50% of R frames), R laps main, lick stop L2
   R 0.954 [0.919 0.988] (n=10); RE high 0.885 [0.834 0.935] (n=10); RE low 0.579 [0.383 0.775] (n=10); RR 0.851 [0.750 0.951] (n=5)
   R-RE high: t=5.05 p=0.0007 (x6 Bonferroni 0.0041, n=10); R-RE low: t=4.28 p=0.0020 (x6 Bonferroni 0.0123, n=10); R-RR: t=3.21 p=0.0325 (x6 Bonferroni 0.1948, n=5); RE high-RE low: t=3.44 p=0.0074 (x6 Bonferroni 0.0447, n=10); RE high-RR: t=0.73 p=0.5070 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-4.14 p=0.0143 (x6 Bonferroni 0.0861, n=5)
--- code decoder (50% of R frames), R laps main, lick stop L3
   R 0.954 [0.919 0.988] (n=10); RE high 0.863 [0.801 0.925] (n=10); RE low 0.560 [0.337 0.783] (n=10); RR 0.851 [0.750 0.951] (n=5)
   R-RE high: t=6.06 p=0.0002 (x6 Bonferroni 0.0011, n=10); R-RE low: t=3.92 p=0.0035 (x6 Bonferroni 0.0210, n=10); R-RR: t=3.21 p=0.0325 (x6 Bonferroni 0.1948, n=5); RE high-RE low: t=2.94 p=0.0166 (x6 Bonferroni 0.0993, n=10); RE high-RR: t=-0.68 p=0.5318 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.71 p=0.0207 (x6 Bonferroni 0.1244, n=5)
--- code decoder (50% of R frames), R laps all tested R laps, lick stop L1
   R 0.977 [0.965 0.988] (n=10); RE high 0.899 [0.857 0.941] (n=10); RE low 0.601 [0.435 0.768] (n=10); RR 0.851 [0.750 0.951] (n=5)
   R-RE high: t=5.14 p=0.0006 (x6 Bonferroni 0.0037, n=10); R-RE low: t=5.21 p=0.0006 (x6 Bonferroni 0.0033, n=10); R-RR: t=4.40 p=0.0117 (x6 Bonferroni 0.0701, n=5); RE high-RE low: t=4.27 p=0.0021 (x6 Bonferroni 0.0125, n=10); RE high-RR: t=2.00 p=0.1160 (x6 Bonferroni 0.6963, n=5); RE low-RR: t=-4.40 p=0.0117 (x6 Bonferroni 0.0701, n=5)
--- code decoder (50% of R frames), R laps all tested R laps, lick stop L2
   R 0.977 [0.965 0.988] (n=10); RE high 0.885 [0.834 0.935] (n=10); RE low 0.579 [0.383 0.775] (n=10); RR 0.851 [0.750 0.951] (n=5)
   R-RE high: t=5.00 p=0.0007 (x6 Bonferroni 0.0044, n=10); R-RE low: t=4.66 p=0.0012 (x6 Bonferroni 0.0071, n=10); R-RR: t=4.40 p=0.0117 (x6 Bonferroni 0.0701, n=5); RE high-RE low: t=3.44 p=0.0074 (x6 Bonferroni 0.0447, n=10); RE high-RR: t=0.73 p=0.5070 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-4.14 p=0.0143 (x6 Bonferroni 0.0861, n=5)
--- code decoder (50% of R frames), R laps all tested R laps, lick stop L3
   R 0.977 [0.965 0.988] (n=10); RE high 0.863 [0.801 0.925] (n=10); RE low 0.560 [0.337 0.783] (n=10); RR 0.851 [0.750 0.951] (n=5)
   R-RE high: t=4.91 p=0.0008 (x6 Bonferroni 0.0050, n=10); R-RE low: t=4.27 p=0.0021 (x6 Bonferroni 0.0125, n=10); R-RR: t=4.40 p=0.0117 (x6 Bonferroni 0.0701, n=5); RE high-RE low: t=2.94 p=0.0166 (x6 Bonferroni 0.0993, n=10); RE high-RR: t=-0.68 p=0.5318 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.71 p=0.0207 (x6 Bonferroni 0.1244, n=5)
--- 60% of R laps, not circular, R laps main, lick stop L1
   R 0.798 [0.671 0.925] (n=10); RE high 0.387 [0.166 0.608] (n=10); RE low -0.446 [-0.839 -0.053] (n=10); RR 0.392 [0.099 0.684] (n=5)
   R-RE high: t=4.84 p=0.0009 (x6 Bonferroni 0.0055, n=10); R-RE low: t=7.74 p=0.0000 (x6 Bonferroni 0.0002, n=10); R-RR: t=2.81 p=0.0483 (x6 Bonferroni 0.2898, n=5); RE high-RE low: t=4.86 p=0.0009 (x6 Bonferroni 0.0054, n=10); RE high-RR: t=0.30 p=0.7765 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.01 p=0.0395 (x6 Bonferroni 0.2372, n=5)
--- 60% of R laps, not circular, R laps main, lick stop L2
   R 0.798 [0.671 0.925] (n=10); RE high 0.309 [0.032 0.585] (n=10); RE low -0.479 [-0.897 -0.062] (n=10); RR 0.392 [0.099 0.684] (n=5)
   R-RE high: t=4.95 p=0.0008 (x6 Bonferroni 0.0047, n=10); R-RE low: t=7.28 p=0.0000 (x6 Bonferroni 0.0003, n=10); R-RR: t=2.81 p=0.0483 (x6 Bonferroni 0.2898, n=5); RE high-RE low: t=4.29 p=0.0020 (x6 Bonferroni 0.0120, n=10); RE high-RR: t=-1.12 p=0.3245 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.00 p=0.0399 (x6 Bonferroni 0.2392, n=5)
--- 60% of R laps, not circular, R laps main, lick stop L3
   R 0.798 [0.671 0.925] (n=10); RE high 0.297 [0.022 0.572] (n=10); RE low -0.545 [-1.016 -0.074] (n=10); RR 0.392 [0.099 0.684] (n=5)
   R-RE high: t=5.34 p=0.0005 (x6 Bonferroni 0.0028, n=10); R-RE low: t=6.71 p=0.0001 (x6 Bonferroni 0.0005, n=10); R-RR: t=2.81 p=0.0483 (x6 Bonferroni 0.2898, n=5); RE high-RE low: t=4.35 p=0.0019 (x6 Bonferroni 0.0111, n=10); RE high-RR: t=-1.29 p=0.2661 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.10 p=0.0362 (x6 Bonferroni 0.2171, n=5)
--- 60% of R laps, not circular, R laps all tested R laps, lick stop L1
   R 0.845 [0.778 0.911] (n=10); RE high 0.387 [0.166 0.608] (n=10); RE low -0.446 [-0.839 -0.053] (n=10); RR 0.392 [0.099 0.684] (n=5)
   R-RE high: t=5.72 p=0.0003 (x6 Bonferroni 0.0017, n=10); R-RE low: t=7.40 p=0.0000 (x6 Bonferroni 0.0002, n=10); R-RR: t=5.48 p=0.0054 (x6 Bonferroni 0.0324, n=5); RE high-RE low: t=4.86 p=0.0009 (x6 Bonferroni 0.0054, n=10); RE high-RR: t=0.30 p=0.7765 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.01 p=0.0395 (x6 Bonferroni 0.2372, n=5)
--- 60% of R laps, not circular, R laps all tested R laps, lick stop L2
   R 0.845 [0.778 0.911] (n=10); RE high 0.309 [0.032 0.585] (n=10); RE low -0.479 [-0.897 -0.062] (n=10); RR 0.392 [0.099 0.684] (n=5)
   R-RE high: t=5.10 p=0.0006 (x6 Bonferroni 0.0039, n=10); R-RE low: t=7.11 p=0.0001 (x6 Bonferroni 0.0003, n=10); R-RR: t=5.48 p=0.0054 (x6 Bonferroni 0.0324, n=5); RE high-RE low: t=4.29 p=0.0020 (x6 Bonferroni 0.0120, n=10); RE high-RR: t=-1.12 p=0.3245 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.00 p=0.0399 (x6 Bonferroni 0.2392, n=5)
--- 60% of R laps, not circular, R laps all tested R laps, lick stop L3
   R 0.845 [0.778 0.911] (n=10); RE high 0.297 [0.022 0.572] (n=10); RE low -0.545 [-1.016 -0.074] (n=10); RR 0.392 [0.099 0.684] (n=5)
   R-RE high: t=5.19 p=0.0006 (x6 Bonferroni 0.0034, n=10); R-RE low: t=6.63 p=0.0001 (x6 Bonferroni 0.0006, n=10); R-RR: t=5.48 p=0.0054 (x6 Bonferroni 0.0324, n=5); RE high-RE low: t=4.35 p=0.0019 (x6 Bonferroni 0.0111, n=10); RE high-RR: t=-1.29 p=0.2661 (x6 Bonferroni 1.0000, n=5); RE low-RR: t=-3.10 p=0.0362 (x6 Bonferroni 0.2171, n=5)

per mouse, Methods decoder, main reading, L1:
            R  RE high  RE low     RR
NR6    0.833    0.829   0.602  0.804
NR14   0.957    0.812   0.486  0.788
NR21   0.990    0.932   0.721  0.914
NR23   0.970    0.915   0.765  0.842
CFC4   0.987    0.953   0.548  0.957
NR32   0.982    0.888   0.334    NaN
NR34   0.950    0.913   0.151    NaN
CFC16  0.956    0.958   0.808    NaN
CFC17  0.990    0.858   0.829    NaN
CFC19  0.986    0.954   0.814    NaN
('dec', 'main', 'L1') uncorrected means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec', 'main', 'L1') Bonferroni x6 means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec', 'main', 'L2') uncorrected means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec', 'main', 'L2') Bonferroni x6 means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec', 'main', 'L3') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec', 'main', 'L3') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec', 'all tested R laps', 'L1') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec', 'all tested R laps', 'L1') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec', 'all tested R laps', 'L2') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec', 'all tested R laps', 'L2') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec', 'all tested R laps', 'L3') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec', 'all tested R laps', 'L3') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec_var', 'main', 'L1') uncorrected means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec_var', 'main', 'L1') Bonferroni x6 means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec_var', 'main', 'L2') uncorrected means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec_var', 'main', 'L2') Bonferroni x6 means within tolerance: True | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec_var', 'main', 'L3') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec_var', 'main', 'L3') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec_var', 'all tested R laps', 'L1') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec_var', 'all tested R laps', 'L1') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec_var', 'all tested R laps', 'L2') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec_var', 'all tested R laps', 'L2') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)
('dec_var', 'all tested R laps', 'L3') uncorrected means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.True_, np.False_)
('dec_var', 'all tested R laps', 'L3') Bonferroni x6 means within tolerance: False | R<RE low, R<RR significant, R vs RE high not: (np.True_, np.False_, np.False_)

Result: partially reproduced. Under lick stop L1 the per-mouse means are R 0.960, RE high 0.901, RE low 0.606 and RR 0.861, all within tolerance (also under L2). RE low is below R (p = 0.0008, 0.0048 with Bonferroni) and RR below R (p = 0.0345 uncorrected, 0.207 with Bonferroni). RE high is not similar to R: the paired t-test gives p = 0.0047 (0.028 with Bonferroni), where the criterion requires p >= 0.05; this holds under every reading and with the code's decoder.

fig1d-decoder-drop-ur¶

A naive Bayes decoder trained on initial R laps predicts position accurately on the final R laps and on the first UR laps, then abruptly underperforms later in UR.

Per mouse: holds when the mean R2 of the first 3 UR laps is within 0.1 of the mean R2 of the held-out R laps and the mean R2 of the RE low laps is below both (criterion; thresholds the reader's).

In [10]:
for s in WT:
    r = lap_r2(s, "Rewarded")
    u = lap_r2(s, "Unrewarded")
    print(s, "held-out R:", " ".join(f"{r[k]:.2f}" for k in sorted(r)))
    print(s, "UR        :", " ".join(f"{u[k]:.2f}" for k in sorted(u)))
need = int(np.ceil(9 / 12 * len(WT)))
print(f"the criterion's 9 of 12 mice (75%) scaled to the {len(WT)} mice here: at least {need} (ceil(0.75 x {len(WT)})); a literal 9 is also shown")
fig1d = {}
for key in ["dec", "dec_var", "dec_lin"]:
    for L in K.LICK_READINGS:
        hold = 0
        rows = []
        for s in WT:
            r = lap_r2(s, "Rewarded", key)
            u = lap_r2(s, "Unrewarded", key)
            ls = RES[s]["beh"]["lick_stop"][L]
            rmean = np.nanmean(list(r.values()))
            first3 = np.nanmean([u[k] for k in sorted(u)[:3]])
            low = np.nanmean([v for k, v in u.items() if k >= ls])
            ok = abs(first3 - rmean) <= 0.1 and low < first3 and low < rmean
            hold += ok
            rows.append((s, round(rmean, 3), round(first3, 3), round(low, 3), ok))
        fig1d[(key, L)] = hold
        print(f"{key} {L}: holds in {hold} of {len(WT)} mice (>= {need}: {hold >= need}; >= 9: {hold >= 9}):", rows)
NR6 held-out R: 1.00 0.99 0.99 0.99 1.00 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.36 0.87 0.96
NR6 UR        : 0.68 0.94 0.95 0.91 0.93 0.94 0.94 0.94 0.88 0.83 0.80 0.22 -0.11 -0.12 0.44 0.84 0.02 0.79 0.86 0.89 0.67 0.89 0.82 0.66 0.72 0.82 0.74 0.62 0.75 0.61 0.58 0.56
NR14 held-out R: 0.99 0.98 0.99 0.88 0.94 0.89 1.00 0.99 0.98 0.98 0.87 0.97
NR14 UR        : 0.99 0.99 0.52 0.54 0.72 0.67 0.95 0.88 0.94 0.92 0.86 0.82 0.95 0.54 0.73 0.87 0.90 0.74 0.51 0.45 -1.23 0.39 -0.22
NR21 held-out R: 0.99 0.99 0.99 0.99 0.99 1.00 0.99 0.99 0.99 0.97 1.00 0.98 0.99 1.00 0.99 0.99
NR21 UR        : 0.91 0.94 0.92 0.97 0.93 0.99 0.99 0.92 0.75 0.97 0.82 0.78 0.37 0.47 0.96 0.61 0.38 0.93 -0.16 0.33 0.91 0.94 0.64 0.62 0.62 0.91 0.80 0.66 0.46 0.97 0.92 0.90
NR23 held-out R: 0.96 0.99 0.99 0.99 1.00 0.99 0.99 0.99 0.98 0.98 0.97 0.99 0.99 0.89 0.99 0.99
NR23 UR        : 0.98 0.85 0.83 0.56 0.66 0.76 0.78 0.87 0.72 0.76 0.97 0.96 0.92 0.56 0.92 0.69 0.55 0.83 -0.16 0.93 0.98 0.81 0.84 0.94 0.85 0.91 0.95 0.98 0.16 0.68 0.89 0.87
CFC4 held-out R: 1.00 1.00 0.99 0.99 1.00 1.00 0.99 1.00 0.99 1.00 0.99 0.99 1.00 0.98 0.99 0.98
CFC4 UR        : 0.99 0.72 0.98 0.98 0.99 0.99 0.98 0.99 0.98 0.78 0.88 0.67 0.79 0.82 0.91 0.88 0.89 0.42 0.15 0.86 0.57 0.45 0.93 0.64 0.76 0.57 0.59 0.33 0.29 0.64 0.59 0.68 0.49 0.65 0.33 0.56 0.53 0.68 0.35 0.53 0.81 0.27 0.18 0.40 0.24 0.23 0.53 0.48 0.28 0.95 0.84 0.72 0.48 0.49 0.28 0.10 0.70 0.44 0.19 0.09 0.14
NR32 held-out R: 0.99 0.99 0.99 1.00 1.00 0.98 0.99 0.99 0.99 0.95 1.00 0.98
NR32 UR        : 0.99 0.96 0.71 0.96 0.85 0.93 0.79 0.90 -1.61 0.92 -2.29 -0.11 0.94 0.95 -0.80 0.59 0.34 0.27 0.86 0.70 0.81
NR34 held-out R: 1.00 1.00 1.00 1.00 0.99 0.88 0.87 0.89 0.96 0.99 0.99 0.91
NR34 UR        : 0.76 0.99 0.98 0.96 0.98 0.99 0.79 0.94 0.95 0.77 0.94 0.94 0.51 -2.00 0.67 0.03 0.75
CFC16 held-out R: 1.00 0.99 0.99 1.00 0.99 0.99 0.99 1.00 0.90 0.99 0.99 0.99 0.90
CFC16 UR        : 0.99 0.93 0.78 0.82 0.77 0.82 0.74 0.97 0.80 0.93 0.98 0.86 0.99 0.91 0.73 -0.18 0.91 0.97 0.96 0.67 0.44 0.95 0.65 0.98 0.95 0.99
CFC17 held-out R: 0.96 1.00 0.99 1.00 0.99 0.98 0.99 0.99 0.99 0.99 0.98 0.99 0.99 0.99 0.99
CFC17 UR        : 0.99 0.68 0.99 0.94 0.96 0.60 0.81 0.97 0.76 0.66 0.83 0.94 0.75 0.95 0.91 0.85 0.76 0.92 0.66 0.90 0.72 0.94 0.55 0.67 0.81 0.85 0.81 0.87 0.90 0.93 0.91 0.84 0.97 0.69 0.95 0.76
CFC19 held-out R: 0.99 1.00 1.00 1.00 1.00 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.98 0.98
CFC19 UR        : 0.98 0.85 0.97 0.96 0.97 0.99 0.96 0.94 0.98 0.91 0.83 0.90 0.78 0.77 0.89 0.20 0.94
the criterion's 9 of 12 mice (75%) scaled to the 10 mice here: at least 8 (ceil(0.75 x 10)); a literal 9 is also shown
dec L1: holds in 8 of 10 mice (>= 8: True; >= 9: False): [('NR6', np.float64(0.948), np.float64(0.853), np.float64(0.602), np.True_), ('NR14', np.float64(0.955), np.float64(0.833), np.float64(0.486), np.False_), ('NR21', np.float64(0.991), np.float64(0.92), np.float64(0.721), np.True_), ('NR23', np.float64(0.981), np.float64(0.886), np.float64(0.765), np.True_), ('CFC4', np.float64(0.992), np.float64(0.895), np.float64(0.548), np.True_), ('NR32', np.float64(0.988), np.float64(0.888), np.float64(0.334), np.True_), ('NR34', np.float64(0.956), np.float64(0.909), np.float64(0.151), np.True_), ('CFC16', np.float64(0.979), np.float64(0.9), np.float64(0.808), np.True_), ('CFC17', np.float64(0.989), np.float64(0.885), np.float64(0.829), np.False_), ('CFC19', np.float64(0.992), np.float64(0.932), np.float64(0.814), np.True_)]
dec L2: holds in 8 of 10 mice (>= 8: True; >= 9: False): [('NR6', np.float64(0.948), np.float64(0.853), np.float64(0.639), np.True_), ('NR14', np.float64(0.955), np.float64(0.833), np.float64(0.455), np.False_), ('NR21', np.float64(0.991), np.float64(0.92), np.float64(0.71), np.True_), ('NR23', np.float64(0.981), np.float64(0.886), np.float64(0.763), np.True_), ('CFC4', np.float64(0.992), np.float64(0.895), np.float64(0.54), np.True_), ('NR32', np.float64(0.988), np.float64(0.888), np.float64(0.297), np.True_), ('NR34', np.float64(0.956), np.float64(0.909), np.float64(-0.008), np.True_), ('CFC16', np.float64(0.979), np.float64(0.9), np.float64(0.809), np.True_), ('CFC17', np.float64(0.989), np.float64(0.885), np.float64(0.829), np.False_), ('CFC19', np.float64(0.992), np.float64(0.932), np.float64(0.8), np.True_)]
dec L3: holds in 8 of 10 mice (>= 8: True; >= 9: False): [('NR6', np.float64(0.948), np.float64(0.853), np.float64(0.682), np.True_), ('NR14', np.float64(0.955), np.float64(0.833), np.float64(0.421), np.False_), ('NR21', np.float64(0.991), np.float64(0.92), np.float64(0.699), np.True_), ('NR23', np.float64(0.981), np.float64(0.886), np.float64(0.771), np.True_), ('CFC4', np.float64(0.992), np.float64(0.895), np.float64(0.535), np.True_), ('NR32', np.float64(0.988), np.float64(0.888), np.float64(0.263), np.True_), ('NR34', np.float64(0.956), np.float64(0.909), np.float64(-0.136), np.True_), ('CFC16', np.float64(0.979), np.float64(0.9), np.float64(0.809), np.True_), ('CFC17', np.float64(0.989), np.float64(0.885), np.float64(0.824), np.False_), ('CFC19', np.float64(0.992), np.float64(0.932), np.float64(0.777), np.True_)]
dec_var L1: holds in 6 of 10 mice (>= 8: False; >= 9: False): [('NR6', np.float64(0.949), np.float64(0.83), np.float64(0.573), np.False_), ('NR14', np.float64(0.951), np.float64(0.823), np.float64(0.47), np.False_), ('NR21', np.float64(0.991), np.float64(0.926), np.float64(0.723), np.True_), ('NR23', np.float64(0.97), np.float64(0.89), np.float64(0.768), np.True_), ('CFC4', np.float64(0.991), np.float64(0.876), np.float64(0.581), np.False_), ('NR32', np.float64(0.987), np.float64(0.874), np.float64(0.319), np.False_), ('NR34', np.float64(0.969), np.float64(0.907), np.float64(0.141), np.True_), ('CFC16', np.float64(0.979), np.float64(0.9), np.float64(0.802), np.True_), ('CFC17', np.float64(0.988), np.float64(0.905), np.float64(0.812), np.True_), ('CFC19', np.float64(0.991), np.float64(0.926), np.float64(0.823), np.True_)]
dec_var L2: holds in 6 of 10 mice (>= 8: False; >= 9: False): [('NR6', np.float64(0.949), np.float64(0.83), np.float64(0.61), np.False_), ('NR14', np.float64(0.951), np.float64(0.823), np.float64(0.439), np.False_), ('NR21', np.float64(0.991), np.float64(0.926), np.float64(0.712), np.True_), ('NR23', np.float64(0.97), np.float64(0.89), np.float64(0.766), np.True_), ('CFC4', np.float64(0.991), np.float64(0.876), np.float64(0.573), np.False_), ('NR32', np.float64(0.987), np.float64(0.874), np.float64(0.281), np.False_), ('NR34', np.float64(0.969), np.float64(0.907), np.float64(-0.018), np.True_), ('CFC16', np.float64(0.979), np.float64(0.9), np.float64(0.803), np.True_), ('CFC17', np.float64(0.988), np.float64(0.905), np.float64(0.815), np.True_), ('CFC19', np.float64(0.991), np.float64(0.926), np.float64(0.806), np.True_)]
dec_var L3: holds in 6 of 10 mice (>= 8: False; >= 9: False): [('NR6', np.float64(0.949), np.float64(0.83), np.float64(0.652), np.False_), ('NR14', np.float64(0.951), np.float64(0.823), np.float64(0.418), np.False_), ('NR21', np.float64(0.991), np.float64(0.926), np.float64(0.701), np.True_), ('NR23', np.float64(0.97), np.float64(0.89), np.float64(0.774), np.True_), ('CFC4', np.float64(0.991), np.float64(0.876), np.float64(0.568), np.False_), ('NR32', np.float64(0.987), np.float64(0.874), np.float64(0.246), np.False_), ('NR34', np.float64(0.969), np.float64(0.907), np.float64(-0.155), np.True_), ('CFC16', np.float64(0.979), np.float64(0.9), np.float64(0.802), np.True_), ('CFC17', np.float64(0.988), np.float64(0.905), np.float64(0.809), np.True_), ('CFC19', np.float64(0.991), np.float64(0.926), np.float64(0.786), np.True_)]
dec_lin L1: holds in 2 of 10 mice (>= 8: False; >= 9: False): [('NR6', np.float64(0.779), np.float64(0.711), np.float64(-0.756), np.True_), ('NR14', np.float64(0.703), np.float64(0.615), np.float64(-0.116), np.True_), ('NR21', np.float64(0.745), np.float64(-0.067), np.float64(0.119), np.False_), ('NR23', np.float64(0.802), np.float64(-0.361), np.float64(-0.674), np.False_), ('CFC4', np.float64(0.958), np.float64(0.835), np.float64(-0.983), np.False_), ('NR32', np.float64(0.875), np.float64(0.281), np.float64(-0.484), np.False_), ('NR34', np.float64(0.787), np.float64(0.56), np.float64(-1.47), np.False_), ('CFC16', np.float64(0.914), np.float64(0.506), np.float64(0.05), np.False_), ('CFC17', np.float64(0.938), np.float64(0.728), np.float64(0.298), np.False_), ('CFC19', np.float64(0.946), np.float64(0.513), np.float64(-0.44), np.False_)]
dec_lin L2: holds in 2 of 10 mice (>= 8: False; >= 9: False): [('NR6', np.float64(0.779), np.float64(0.711), np.float64(-0.639), np.True_), ('NR14', np.float64(0.703), np.float64(0.615), np.float64(-0.119), np.True_), ('NR21', np.float64(0.745), np.float64(-0.067), np.float64(0.089), np.False_), ('NR23', np.float64(0.802), np.float64(-0.361), np.float64(-0.644), np.False_), ('CFC4', np.float64(0.958), np.float64(0.835), np.float64(-1.02), np.False_), ('NR32', np.float64(0.875), np.float64(0.281), np.float64(-0.549), np.False_), ('NR34', np.float64(0.787), np.float64(0.56), np.float64(-1.654), np.False_), ('CFC16', np.float64(0.914), np.float64(0.506), np.float64(0.037), np.False_), ('CFC17', np.float64(0.938), np.float64(0.728), np.float64(0.298), np.False_), ('CFC19', np.float64(0.946), np.float64(0.513), np.float64(-0.59), np.False_)]
dec_lin L3: holds in 2 of 10 mice (>= 8: False; >= 9: False): [('NR6', np.float64(0.779), np.float64(0.711), np.float64(-0.594), np.True_), ('NR14', np.float64(0.703), np.float64(0.615), np.float64(-0.15), np.True_), ('NR21', np.float64(0.745), np.float64(-0.067), np.float64(0.053), np.False_), ('NR23', np.float64(0.802), np.float64(-0.361), np.float64(-0.649), np.False_), ('CFC4', np.float64(0.958), np.float64(0.835), np.float64(-1.031), np.False_), ('NR32', np.float64(0.875), np.float64(0.281), np.float64(-0.629), np.False_), ('NR34', np.float64(0.787), np.float64(0.56), np.float64(-1.99), np.False_), ('CFC16', np.float64(0.914), np.float64(0.506), np.float64(0.026), np.False_), ('CFC17', np.float64(0.938), np.float64(0.728), np.float64(0.276), np.False_), ('CFC19', np.float64(0.946), np.float64(0.513), np.float64(-0.763), np.False_)]

Result: reproduced. With the decoder of the Methods (60% of R laps, circular track), the first 3 UR laps decode within 0.1 of the held-out R laps and RE low laps decode worse than both in 8 of the 10 mice under each lick stop reading. The criterion's 9 of 12 mice is applied as the same fraction, 75%, of the 10 mice here, which needs at least 8 (ceil(0.75 x 10)); a literal 9 is not reached. The code's decoder gives 6 of 10. Without the circular treatment, 2 of 10.

fig1e-decoder-follows-licking¶

Averaged over 12 mice, lap by lap decoder R2 in UR falls as an S-shaped curve around the lap where pre-licking reaches zero; a reverse Boltzmann sigmoid fits the mean R2 with r = 0.94, and its point of maximum change is at lap 10.

In [11]:
from scipy.optimize import curve_fit

def boltz(x, top, bottom, v50, slope):
    return bottom + (top - bottom) / (1 + np.exp((x - v50) / slope))

def roll3(x):
    return pd.Series(x).rolling(3, min_periods=1).mean().to_numpy()

NL = 15
r2_mat = np.full((len(WT), NL), np.nan)
lk_mat = np.full((len(WT), NL), np.nan)
for i, s in enumerate(WT):
    u = lap_r2(s, "Unrewarded")
    z = RES[s]["beh"]["ur_zone_licks"]
    arr = np.array([u.get(k, np.nan) for k in range(len(z))])
    sm = roll3(arr)[:NL]
    r2_mat[i, :len(sm)] = sm
    lz = roll3(z.astype(float))[:NL]
    lk_mat[i, :len(lz)] = lz
mean_r2 = np.nanmean(r2_mat, axis=0)
mean_lk = np.nanmean(lk_mat, axis=0)
x = np.arange(1, NL + 1)
p, _ = curve_fit(boltz, x, mean_r2, p0=[mean_r2[:3].mean(), mean_r2[-3:].mean(), 8, 1], maxfev=20000)
fit = boltz(x, *p)
r_fit = np.corrcoef(fit, mean_r2)[0, 1]
R2_fit = 1 - ((mean_r2 - fit) ** 2).sum() / ((mean_r2 - mean_r2.mean()) ** 2).sum()
xf = np.linspace(1, NL, 2001)
d2 = np.gradient(np.gradient(boltz(xf, *p), xf), xf)
zc = np.where(np.diff(np.sign(d2)) != 0)[0]
lap_max = xf[zc[0]] if len(zc) else np.nan
lk_norm = mean_lk / np.nanmax(mean_lk)
lk_min_lap = int(x[np.argmax(lk_norm <= lk_norm.min() + 1e-9)])
print("mean R2 per UR lap (3-lap rolling, mice present):", np.round(mean_r2, 3))
print("mice per lap:", np.isfinite(r2_mat).sum(0))
print("mean zone licks per lap (rolling):", np.round(mean_lk, 2))
print(f"sigmoid params top {p[0]:.3f} bottom {p[1]:.3f} v50 {p[2]:.2f} slope {p[3]:.2f}")
print(f"fit r = {r_fit:.3f}, fit R2 = {R2_fit:.3f} (paper 0.94 +/- 0.094)")
print(f"first zero of second derivative at lap {lap_max:.2f} (rounded {np.round(lap_max):.0f}; paper 10 +/- 1)")
print(f"normalised lick curve first reaches its minimum at lap {lk_min_lap}; distance to the lap of maximum change {abs(lk_min_lap - np.round(lap_max)):.0f}")
mean R2 per UR lap (3-lap rolling, mice present): [0.924 0.905 0.89  0.869 0.865 0.868 0.872 0.891 0.807 0.798 0.675 0.703
 0.65  0.591 0.57 ]
mice per lap: [10 10 10 10 10 10 10 10 10 10 10 10 10 10 10]
mean zone licks per lap (rolling): [11.3   9.7   8.4   5.73  3.73  2.13  1.43  0.93  0.77  0.77  0.63  0.5
  0.1   0.03  0.03]
sigmoid params top 0.898 bottom 0.537 v50 11.23 slope 1.74
fit r = 0.979, fit R2 = 0.959 (paper 0.94 +/- 0.094)
first zero of second derivative at lap 11.23 (rounded 11; paper 10 +/- 1)
normalised lick curve first reaches its minimum at lap 14; distance to the lap of maximum change 3

Result: reproduced. The reverse Boltzmann fit to the mean UR R2 has r = 0.979 (R2 of the fit 0.959; paper 0.94), its second derivative first reaches zero at lap 11.23, rounded to 11 (paper 10 +/- 1), and the smoothed lick curve first reaches its minimum at lap 14, 3 laps from it, within the 3 laps the criterion allows.

fig1g-decoder-error-all-locations¶

In RE low laps decoder error increases at all locations along the track (start, middle and end), not only near the reward; in RR the error decreases across the track.

Error per frame = distance in cm between true and decoded position (circular), averaged per mouse over the frames of each condition in the start (0 to 50 cm), middle (50 to 150 cm) and end (150 to 200 cm) groups.

In [12]:
GROUPS = {"start": (0, 10), "middle": (10, 30), "end": (30, 40)}

def group_error(s, cond, laps_sel, key="dec"):
    out = {}
    recs = [d for d in RES[s][key][cond] if laps_sel(d["lap"])]
    if not recs:
        return {g: np.nan for g in GROUPS}
    bins = np.concatenate([d["bins"] for d in recs])
    err = np.concatenate([d["err_cm"] for d in recs])
    for g, (a, b) in GROUPS.items():
        m = (bins >= a) & (bins < b)
        out[g] = err[m].mean() if m.any() else np.nan
    return out

fig1g = {}
for key in ["dec", "dec_var"]:
    for L in K.LICK_READINGS:
        rows = {}
        for s in WT:
            ls = RES[s]["beh"]["lick_stop"][L]
            rows[(s, "R")] = group_error(s, "Rewarded", lambda l: True, key)
            rows[(s, "RE low")] = group_error(s, "Unrewarded", lambda l, ls=ls: l >= ls, key)
            if "ReRewarded" in RES[s][key]:
                rows[(s, "RR")] = group_error(s, "ReRewarded", lambda l: True, key)
        df = pd.DataFrame(rows).T
        res_g = {}
        for g in GROUPS:
            r = df.xs("R", level=1)[g]
            lo = df.xs("RE low", level=1)[g].reindex(r.index)
            t = stats.ttest_rel(lo, r)
            rr = df.xs("RR", level=1)[g]
            t2 = stats.ttest_rel(rr, r.reindex(rr.index))
            res_g[g] = (r.mean(), lo.mean(), rr.mean(), t.statistic, t.pvalue, t2.statistic, t2.pvalue)
        fig1g[(key, L)] = res_g
        print(f"--- {key} {L}")
        for g, v in res_g.items():
            print(f"  {g:6s}: R {v[0]:.1f} cm, RE low {v[1]:.1f} cm, RR {v[2]:.1f} cm (5 mice); RE low vs R t={v[3]:.2f} p={v[4]:.4f}; RR vs R t={v[5]:.2f} p={v[6]:.4f}")
        n_sig = sum(v[1] > v[0] and v[4] < 0.01 for v in res_g.values())
        print(f"  groups with RE low error higher at p < 0.01: {n_sig} of 3")
print("error range over mice and groups (cm):", round(float(np.nanmin(df.values)), 1), "to", round(float(np.nanmax(df.values)), 1))
--- dec L1
  start : R 4.9 cm, RE low 17.1 cm, RR 12.1 cm (5 mice); RE low vs R t=13.22 p=0.0000; RR vs R t=3.70 p=0.0208
  middle: R 5.6 cm, RE low 33.9 cm, RR 21.0 cm (5 mice); RE low vs R t=6.49 p=0.0001; RR vs R t=7.47 p=0.0017
  end   : R 4.4 cm, RE low 17.3 cm, RR 11.6 cm (5 mice); RE low vs R t=8.77 p=0.0000; RR vs R t=4.33 p=0.0123
  groups with RE low error higher at p < 0.01: 3 of 3
--- dec L2
  start : R 4.9 cm, RE low 17.4 cm, RR 12.1 cm (5 mice); RE low vs R t=13.50 p=0.0000; RR vs R t=3.70 p=0.0208
  middle: R 5.6 cm, RE low 34.4 cm, RR 21.0 cm (5 mice); RE low vs R t=6.38 p=0.0001; RR vs R t=7.47 p=0.0017
  end   : R 4.4 cm, RE low 18.0 cm, RR 11.6 cm (5 mice); RE low vs R t=6.86 p=0.0001; RR vs R t=4.33 p=0.0123
  groups with RE low error higher at p < 0.01: 3 of 3
--- dec L3
  start : R 4.9 cm, RE low 17.2 cm, RR 12.1 cm (5 mice); RE low vs R t=11.94 p=0.0000; RR vs R t=3.70 p=0.0208
  middle: R 5.6 cm, RE low 34.8 cm, RR 21.0 cm (5 mice); RE low vs R t=6.24 p=0.0002; RR vs R t=7.47 p=0.0017
  end   : R 4.4 cm, RE low 18.1 cm, RR 11.6 cm (5 mice); RE low vs R t=7.28 p=0.0000; RR vs R t=4.33 p=0.0123
  groups with RE low error higher at p < 0.01: 3 of 3
--- dec_var L1
  start : R 5.0 cm, RE low 17.2 cm, RR 12.4 cm (5 mice); RE low vs R t=12.43 p=0.0000; RR vs R t=3.95 p=0.0168
  middle: R 6.0 cm, RE low 34.0 cm, RR 21.8 cm (5 mice); RE low vs R t=6.35 p=0.0001; RR vs R t=6.07 p=0.0037
  end   : R 4.6 cm, RE low 17.7 cm, RR 12.2 cm (5 mice); RE low vs R t=8.50 p=0.0000; RR vs R t=4.38 p=0.0119
  groups with RE low error higher at p < 0.01: 3 of 3
--- dec_var L2
  start : R 5.0 cm, RE low 17.5 cm, RR 12.4 cm (5 mice); RE low vs R t=12.14 p=0.0000; RR vs R t=3.95 p=0.0168
  middle: R 6.0 cm, RE low 34.5 cm, RR 21.8 cm (5 mice); RE low vs R t=6.27 p=0.0001; RR vs R t=6.07 p=0.0037
  end   : R 4.6 cm, RE low 18.4 cm, RR 12.2 cm (5 mice); RE low vs R t=6.85 p=0.0001; RR vs R t=4.38 p=0.0119
  groups with RE low error higher at p < 0.01: 3 of 3
--- dec_var L3
  start : R 5.0 cm, RE low 17.4 cm, RR 12.4 cm (5 mice); RE low vs R t=11.24 p=0.0000; RR vs R t=3.95 p=0.0168
  middle: R 6.0 cm, RE low 34.9 cm, RR 21.8 cm (5 mice); RE low vs R t=6.14 p=0.0002; RR vs R t=6.07 p=0.0037
  end   : R 4.6 cm, RE low 18.3 cm, RR 12.2 cm (5 mice); RE low vs R t=7.32 p=0.0000; RR vs R t=4.38 p=0.0119
  groups with RE low error higher at p < 0.01: 3 of 3
error range over mice and groups (cm): 2.9 to 55.7

Result: reproduced. Under L1, decoder error in RE low is higher than in R at the start (17.1 against 4.9 cm, p < 0.0001), middle (33.9 against 5.6 cm, p = 0.0001) and end (17.3 against 4.4 cm, p < 0.0001) of the track, in 3 of 3 groups under every lick stop reading and with the code's decoder. RR errors (12.1, 21.0 and 11.6 cm in the 5 mice) lie between R and RE low.

supp4-lickstop-definition-robust¶

The drop in decoder R2 from RE high to RE low holds whether the lick stop is the first no-lick lap or after 2 or 4 consecutive no-lick laps, or when laps are grouped by presence of licks; with 6 consecutive no-lick laps the result differs. Median R2 is 0.96 in R and 0.80 in RR, and about 0.87 to 0.93 in RE high and 0.69 to 0.70 in RE low across definitions.

In [13]:
def r2_split(s, ls=None, lick_mask=None, key="dec"):
    u = lap_r2(s, "Unrewarded", key)
    if lick_mask is not None:
        hi = [v for k, v in u.items() if lick_mask[k]]
        lo = [v for k, v in u.items() if not lick_mask[k]]
    else:
        hi = [v for k, v in u.items() if k < ls]
        lo = [v for k, v in u.items() if k >= ls]
    return (np.nanmean(hi) if hi else np.nan), (np.nanmean(lo) if lo else np.nan)

supp4 = {}
for start in ["first", "Nth", "after"]:
    rows = []
    for name, n in [("first no-lick lap", 1), ("2 laps", 2), ("4 laps", 4), ("6 laps", 6), ("presence of licks", None)]:
        his, los = [], []
        for s in WT:
            z = RES[s]["beh"]["ur_zone_licks"]
            if n is None:
                hi, lo = r2_split(s, lick_mask=z > 0)
            else:
                L = {"first": "L1", "Nth": "L2", "after": "L3"}[start]
                ls = K.lick_stop(z > 0, n, L)
                hi, lo = r2_split(s, ls)
            his.append(hi)
            los.append(lo)
        his, los = np.array(his), np.array(los)
        ok = np.isfinite(his) & np.isfinite(los)
        t = stats.ttest_rel(his[ok], los[ok])
        rows.append({"definition": name, "n mice": int(ok.sum()), "median RE high": np.nanmedian(his), "median RE low": np.nanmedian(los),
                     "t": t.statistic, "p": t.pvalue, "p x5 Bonferroni": min(1, 5 * t.pvalue)})
    df = pd.DataFrame(rows)
    supp4[start] = df
    print(f"--- RE low starts at the {start} lap of the run")
    print(df.round(4).to_string(index=False))
medR = np.median([per_mouse_r2(s, "L1")["R"] for s in WT])
medRR = np.nanmedian([per_mouse_r2(s, "L1")["RR"] for s in WT])
print(f"median R {medR:.3f} (paper 0.96 +/- 0.096), median RR {medRR:.3f} (paper 0.80 +/- 0.08)")
for start, df in supp4.items():
    d = df[df.definition != "6 laps"]
    ok_t = (d["p x5 Bonferroni"] < 0.05).all() and (d["median RE high"] > d["median RE low"]).all()
    ok_hi = d["median RE high"].between(0.87 - 0.087, 0.93 + 0.093).all()
    ok_lo = d["median RE low"].between(0.69 - 0.069, 0.70 + 0.07).all()
    print(f"{start}: tests significant for first/2/4/presence: {ok_t}; RE high medians in 0.783 to 1.023: {ok_hi}; RE low medians in 0.621 to 0.77: {ok_lo}")
--- RE low starts at the first lap of the run
       definition  n mice  median RE high  median RE low      t      p  p x5 Bonferroni
first no-lick lap      10          0.9121         0.6711 4.6565 0.0012           0.0060
           2 laps      10          0.9143         0.6613 4.1908 0.0023           0.0117
           4 laps      10          0.9143         0.6613 4.1908 0.0023           0.0117
           6 laps      10          0.9143         0.6613 4.1908 0.0023           0.0117
presence of licks      10          0.9105         0.6699 4.5207 0.0014           0.0072
--- RE low starts at the Nth lap of the run
       definition  n mice  median RE high  median RE low      t      p  p x5 Bonferroni
first no-lick lap      10          0.9121         0.6711 4.6565 0.0012           0.0060
           2 laps      10          0.9030         0.6748 3.4205 0.0076           0.0381
           4 laps      10          0.8563         0.6931 3.0773 0.0132           0.0660
           6 laps      10          0.8351         0.7233 2.1513 0.0599           0.2996
presence of licks      10          0.9105         0.6699 4.5207 0.0014           0.0072
--- RE low starts at the after lap of the run
       definition  n mice  median RE high  median RE low      t      p  p x5 Bonferroni
first no-lick lap      10          0.9030         0.6631 4.3181 0.0019           0.0097
           2 laps      10          0.8828         0.6904 2.9756 0.0156           0.0778
           4 laps      10          0.8433         0.6873 3.0914 0.0129           0.0645
           6 laps       9          0.8248         0.6984 2.7755 0.0241           0.1204
presence of licks      10          0.9105         0.6699 4.5207 0.0014           0.0072
median R 0.976 (paper 0.96 +/- 0.096), median RR 0.842 (paper 0.80 +/- 0.08)
first: tests significant for first/2/4/presence: True; RE high medians in 0.783 to 1.023: True; RE low medians in 0.621 to 0.77: True
Nth: tests significant for first/2/4/presence: False; RE high medians in 0.783 to 1.023: True; RE low medians in 0.621 to 0.77: True
after: tests significant for first/2/4/presence: False; RE high medians in 0.783 to 1.023: True; RE low medians in 0.621 to 0.77: True

Result: reproduced. With RE low starting at the first lap of the run (the first start reading), RE low R2 is below RE high for the first no-lick lap, 2 and 4 laps and the grouping by licks (Bonferroni p 0.006 to 0.012), RE high medians are 0.91 and RE low medians 0.66 to 0.67, inside the widened ranges, and the median R (0.976) and RR (0.842) are within tolerance. Under the other two start readings the 4-lap test is not significant after correction (p = 0.066 and 0.065). The 6-lap definition gives p = 0.30 (Nth reading) and 0.12 (after reading) after correction; under the first reading it is the same split as 2 and 4 laps.

supp4-lapwise-r2¶

Over all laps pooled, decoder R2 is 0.97 (SD 0.046) in R (399 laps), 0.86 (0.14) in RE high (90 laps), 0.56 (0.34) in RE low (244 laps) and 0.79 (0.19) in RR (121 laps).

In [14]:
for L in K.LICK_READINGS:
    pools = {"R held-out": [], "R all laps (incl. training)": [], "RE high": [], "RE low": [], "RR": []}
    for s in WT:
        ls = RES[s]["beh"]["lick_stop"][L]
        pools["R held-out"] += list(lap_r2(s, "Rewarded").values())
        pools["R all laps (incl. training)"] += list(lap_r2(s, "Rewarded").values()) + list(lap_r2(s, "Rewarded_train").values())
        u = lap_r2(s, "Unrewarded")
        pools["RE high"] += [v for k, v in u.items() if k < ls]
        pools["RE low"] += [v for k, v in u.items() if k >= ls]
        if "ReRewarded" in RES[s]["dec"]:
            pools["RR"] += list(lap_r2(s, "ReRewarded").values())
    print(f"--- {L}: " + "; ".join(f"{k}: {len(v)} laps, mean {np.nanmean(v):.3f} (SD {np.nanstd(v, ddof=1):.3f})" for k, v in pools.items()))
    print("    R2 range over pooled laps:", round(min(np.nanmin(v) for v in pools.values()), 2), "to", round(max(np.nanmax(v) for v in pools.values()), 2))
print("total complete R laps:", sum(len(K.complete_laps(RES[s]['beh']['conds']['Rewarded'])) for s in WT))
--- L1: R held-out: 145 laps, mean 0.977 (SD 0.059); R all laps (incl. training): 353 laps, mean 0.990 (SD 0.039); RE high: 66 laps, mean 0.888 (SD 0.144); RE low: 231 laps, mean 0.646 (SD 0.436); RR: 141 laps, mean 0.869 (SD 0.155)
    R2 range over pooled laps: -2.29 to 1.0
--- L2: R held-out: 145 laps, mean 0.977 (SD 0.059); R all laps (incl. training): 353 laps, mean 0.990 (SD 0.039); RE high: 76 laps, mean 0.877 (SD 0.178); RE low: 221 laps, mean 0.639 (SD 0.439); RR: 141 laps, mean 0.869 (SD 0.155)
    R2 range over pooled laps: -2.29 to 1.0
--- L3: R held-out: 145 laps, mean 0.977 (SD 0.059); R all laps (incl. training): 353 laps, mean 0.990 (SD 0.039); RE high: 86 laps, mean 0.858 (SD 0.207); RE low: 211 laps, mean 0.635 (SD 0.444); RR: 141 laps, mean 0.869 (SD 0.155)
    R2 range over pooled laps: -2.29 to 1.0
total complete R laps: 353

Result: partially reproduced. The pooled lap means are within the stated SDs under every reading (L3: R 0.977 held out or 0.990 with training laps, RE high 0.858, RE low 0.635, RR 0.869). The lap counts are not: R has 353 complete laps (paper 399), RE low 211 to 231 (244 +/- 24 needs 220 or more; L1 gives 231 but then RE high has 66 against 90 +/- 9), and RR 141 (121 +/- 12). The dandiset holds 10 of the 12 mice.

supp5-not-running-speed¶

The drop in decoder performance in UR is not due to running; 70% of UR laps (233 of 334) have lap times matched to R, and decoder R2 falls in matched laps as in slower laps (median R 0.96, matched 0.76, slower 0.71). Matched laps take 7.34 s in R and 7.4 s in UR, slower laps 20 s.

In [15]:
def running_lap_times(c):
    """Code variant (GetBehaviorStats_withGoodrunning.py, good_lap_time): running frames only."""
    return np.array([(c["speed"][s:e] >= K.RUN_SPEED).sum() / K.FS for s, e in K.complete_laps(c)])

for reading in ["R min to max", "R 5th to 95th percentile", "R min to max, running time only (code)"]:
    n_m = n_s = 0
    tm_r, tm_u, ts_u = [], [], []
    med = {"R": [], "matched": [], "slower": []}
    for s in WT:
        lt = RES[s]["beh"]["lap_times"]
        r, u = lt["Rewarded"], lt["Unrewarded"]
        if "code" in reading:
            r = running_lap_times(RES[s]["beh"]["conds"]["Rewarded"])
            u = running_lap_times(RES[s]["beh"]["conds"]["Unrewarded"])
        lo, hi = (np.nanmin(r), np.nanmax(r)) if reading.startswith("R min") else tuple(np.nanpercentile(r, [5, 95]))
        matched = (u >= lo) & (u <= hi)
        n_m += matched.sum()
        n_s += (~matched).sum()
        tm_r += list(r)
        tm_u += list(u[matched])
        ts_u += list(u[~matched])
        ur = lap_r2(s, "Unrewarded")
        med["R"].append(np.nanmean(list(lap_r2(s, "Rewarded").values())))
        med["matched"].append(np.nanmean([ur[k] for k in ur if matched[k]]) if any(matched[k] for k in ur) else np.nan)
        med["slower"].append(np.nanmean([ur[k] for k in ur if not matched[k]]) if any(not matched[k] for k in ur) else np.nan)
    w = stats.wilcoxon(np.array(med["R"]), np.array(med["matched"]))
    print(f"--- {reading}: matched {n_m}, slower {n_s} of {n_m + n_s} UR laps ({100 * n_m / (n_m + n_s):.0f}% matched)")
    print(f"    lap time R {np.nanmean(tm_r):.2f} s, matched UR {np.nanmean(tm_u):.2f} s, slower UR {np.nanmean(ts_u):.2f} s")
    print(f"    median over mice of per-mouse mean R2: R {np.nanmedian(med['R']):.3f}, matched {np.nanmedian(med['matched']):.3f}, slower {np.nanmedian(med['slower']):.3f}")
    print(f"    R vs matched Wilcoxon W={w.statistic:.1f} p={w.pvalue:.4f} (x3 Bonferroni {min(1, 3 * w.pvalue):.4f}), n = {len(WT)} mice")
--- R min to max: matched 172, slower 125 of 297 UR laps (58% matched)
    lap time R 12.23 s, matched UR 13.38 s, slower UR 28.19 s
    median over mice of per-mouse mean R2: R 0.985, matched 0.763, slower 0.695
    R vs matched Wilcoxon W=0.0 p=0.0020 (x3 Bonferroni 0.0059), n = 10 mice
--- R 5th to 95th percentile: matched 118, slower 179 of 297 UR laps (40% matched)
    lap time R 12.23 s, matched UR 12.00 s, slower UR 24.61 s
    median over mice of per-mouse mean R2: R 0.985, matched 0.795, slower 0.659
    R vs matched Wilcoxon W=0.0 p=0.0020 (x3 Bonferroni 0.0059), n = 10 mice
--- R min to max, running time only (code): matched 203, slower 94 of 297 UR laps (68% matched)
    lap time R 8.50 s, matched UR 9.16 s, slower UR 15.03 s
    median over mice of per-mouse mean R2: R 0.985, matched 0.826, slower 0.463
    R vs matched Wilcoxon W=0.0 p=0.0020 (x3 Bonferroni 0.0059), n = 10 mice

Result: partially reproduced. Decoder R2 falls in UR laps matched to R in lap time as it does in slower laps (median over mice R 0.985, matched 0.763, slower 0.695, all within tolerance; R against matched Wilcoxon p = 0.0059 after Bonferroni). The counts and times are not reproduced: 172 matched and 125 slower laps of 297 (paper 233 and 101), and lap times of 12.23 s in R and 13.38 s in matched UR laps (paper 7.34 and 7.4 s). Counting running time only, as the code does, gives 203 and 94 laps and 8.50 s and 9.16 s, still outside. The timing check in the data check section shows that the 30 Hz clock agrees with the 1.5 s reward pause, so the longer lap times are not a clock error.

approach-ratio¶

Mice slow down approaching the track end; the middle to end velocity ratio is 1.01 in the dark, 1.3 in R and 1.22 in UR, so approach behaviour is an engagement readout.

The dark sessions are not in the dandiset; R and UR are tested. Ratio per lap = mean binned velocity at 100 to 150 cm over 175 to 200 cm. The Methods compute velocity "at each point along the 2 m track", so the main reading takes the lap from its start to the first frame at the track end and leaves out the 1.5 s pause after the reward, during which the mouse stands at 200 cm (first version of this attempt: the whole lap, now a variant). The code's ratio (velocity normalised to its peak, bins 20 to 29 over 28 to 37) is a variant. A synthetic lap at the end of the cell checks the helper.

In [16]:
appr = {}
for key, label in RATIO_LABELS.items():
    for cond in ["Rewarded", "Unrewarded"]:
        pooled = np.concatenate([RES[s]["beh"][key][cond] for s in WT])
        per_mouse = [np.nanmean(RES[s]["beh"][key][cond]) for s in WT]
        m1, h1 = mean_ci(pooled)
        m2, h2 = mean_ci(per_mouse)
        appr[(key, cond)] = (m1, h1, m2, h2)
        print(f"{label:36s} {cond:10s}: over laps {m1:.3f} +/- {h1:.3f} (n = {np.isfinite(pooled).sum()} laps, {np.isnan(pooled).sum()} undefined); "
              f"over mice {m2:.3f} +/- {h2:.3f} (n = {len(WT)}); median over laps {np.nanmedian(pooled):.3f}; range {np.nanmin(pooled):.2f} to {np.nanmax(pooled):.2f}")
print("laps with an undefined ratio (NaN) per mouse, R / UR:")
print(pd.DataFrame({s: {RATIO_LABELS[k]: f"{np.isnan(RES[s]['beh'][k]['Rewarded']).sum()} / {np.isnan(RES[s]['beh'][k]['Unrewarded']).sum()}" for k in RATIO_LABELS} for s in WT}).to_string())
print("per mouse mean ratio R / UR:")
print(pd.DataFrame({s: {RATIO_LABELS[k]: f"{np.nanmean(RES[s]['beh'][k]['Rewarded']):.2f} / {np.nanmean(RES[s]['beh'][k]['Unrewarded']):.2f}" for k in RATIO_LABELS} for s in WT}).to_string())
# synthetic check of the helper: constant 60 cm/s up to 150 cm, then a linear fall to 30 cm/s at 200 cm, then a 1.5 s pause
x = [0.0]
while x[-1] < 200:
    v = 60.0 if x[-1] < 150 else 60.0 - 30.0 * (x[-1] - 150) / 50
    x.append(min(200.0, x[-1] + v / K.FS))
x = np.array(x + [200.0] * 45)
syn = {"pos": x, "speed": np.r_[np.diff(x) * K.FS, 0.0]}
true = 60.0 / np.mean([60.0 - 30.0 * (p - 150) / 50 for p in np.linspace(175, 200, 101)])
print(f"synthetic lap: true ratio {true:.3f}; helper over the traversal {K.approach_ratio(syn, 0, len(x), True):.3f}, over the whole lap with the pause {K.approach_ratio(syn, 0, len(x)):.3f}")
Methods ratio, traversal only (main) Rewarded  : over laps 2.214 +/- 0.092 (n = 352 laps, 1 undefined); over mice 2.224 +/- 0.539 (n = 10); median over laps 1.881; range 0.71 to 5.51
Methods ratio, traversal only (main) Unrewarded: over laps 1.745 +/- 0.072 (n = 297 laps, 0 undefined); over mice 1.735 +/- 0.104 (n = 10); median over laps 1.642; range 0.45 to 4.97
Methods ratio, whole lap             Rewarded  : over laps 5.165 +/- 0.887 (n = 279 laps, 74 undefined); over mice 5.487 +/- 2.345 (n = 10); median over laps 3.375; range 0.77 to 64.92
Methods ratio, whole lap             Unrewarded: over laps 4.444 +/- 1.448 (n = 221 laps, 76 undefined); over mice 4.485 +/- 1.702 (n = 10); median over laps 2.443; range -0.23 to 150.72
code ratio, traversal only           Rewarded  : over laps 1.259 +/- 0.030 (n = 352 laps, 1 undefined); over mice 1.257 +/- 0.176 (n = 10); median over laps 1.201; range 0.73 to 3.27
code ratio, traversal only           Unrewarded: over laps 1.199 +/- 0.025 (n = 297 laps, 0 undefined); over mice 1.170 +/- 0.065 (n = 10); median over laps 1.171; range 0.60 to 2.10
code ratio, whole lap                Rewarded  : over laps 7.458 +/- 13.597 (n = 352 laps, 1 undefined); over mice 8.646 +/- 18.492 (n = 10); median over laps 1.232; range -341.41 to 2409.57
code ratio, whole lap                Unrewarded: over laps 1.034 +/- 1.302 (n = 297 laps, 0 undefined); over mice 0.997 +/- 1.763 (n = 10); median over laps 1.226; range -147.81 to 61.37
laps with an undefined ratio (NaN) per mouse, R / UR:
                                         NR6   NR14   NR21    NR23    CFC4   NR32   NR34   CFC16  CFC17  CFC19
Methods ratio, traversal only (main)   0 / 0  0 / 0  0 / 0   0 / 0   0 / 0  1 / 0  0 / 0   0 / 0  0 / 0  0 / 0
Methods ratio, whole lap              8 / 16  6 / 5  9 / 4  7 / 11  6 / 14  5 / 3  3 / 4  14 / 8  8 / 9  8 / 2
code ratio, traversal only             0 / 0  0 / 0  0 / 0   0 / 0   0 / 0  1 / 0  0 / 0   0 / 0  0 / 0  0 / 0
code ratio, whole lap                  0 / 0  0 / 0  0 / 0   0 / 0   0 / 0  1 / 0  0 / 0   0 / 0  0 / 0  0 / 0
per mouse mean ratio R / UR:
                                              NR6          NR14         NR21         NR23         CFC4         NR32         NR34         CFC16          CFC17        CFC19
Methods ratio, traversal only (main)  1.64 / 1.87   1.95 / 1.80  2.74 / 1.59  1.85 / 1.71  3.43 / 1.77  2.77 / 1.39  1.47 / 1.81   3.26 / 1.83    1.69 / 1.85  1.45 / 1.73
Methods ratio, whole lap              5.08 / 8.11   4.98 / 6.80  5.03 / 3.34  3.28 / 2.89  7.63 / 3.23  9.87 / 3.03  2.51 / 2.53  11.73 / 2.95    2.71 / 8.65  2.05 / 3.30
code ratio, traversal only            1.16 / 1.22   1.25 / 1.18  1.31 / 1.19  1.15 / 1.19  1.77 / 1.32  1.39 / 1.09  0.90 / 0.99   1.46 / 1.22    1.16 / 1.21  1.02 / 1.10
code ratio, whole lap                 1.86 / 1.68  81.69 / 1.73  1.43 / 1.67  1.49 / 1.31  2.27 / 3.25  1.43 / 1.45  1.16 / 1.16   1.74 / 2.11  -8.13 / -5.82  1.51 / 1.42
synthetic lap: true ratio 1.600; helper over the traversal 1.601, over the whole lap with the pause 1.895

Result: not reproduced. Mice slow down approaching the end in both conditions and more in R than in UR, but the Methods ratio over the traversal is 2.214 +/- 0.092 in R (352 laps) and 1.745 +/- 0.072 in UR (297 laps), against 1.3 +/- 0.02 and 1.22 +/- 0.02 (over mice 2.224 and 1.735). Over the whole lap, which takes in the pause, the ratio is 5.17 and 4.44 with 74 and 76 laps undefined. The code's ratio over the traversal is closest, 1.259 and 1.199, still outside the tolerances of 0.02. On the synthetic lap the helper over the traversal returns the true ratio and the whole-lap reading overstates it. The dark sessions are not in the dandiset.

engaged-lap-counts¶

Approach behaviour stays on most UR laps after licking stops; 170 of 244 RE low laps (70%) are engaged and 74 (30%) disengaged, with 90 RE high laps.

In [17]:
eng_counts = {}
for ekey in ENG_KEYS:
  print("=== engaged laps by the", RATIO_LABELS[ekey])
  for L in K.LICK_READINGS:
      n_hi = n_eng = n_dis = n_nan = 0
      r_eng, r_dis = [], []
      per = []
      for s in WT:
          b = RES[s]["beh"]
          ls = b["lick_stop"][L]
          e, d = b["engaged_ur"][ekey][ls:], b["disengaged_ur"][ekey][ls:]
          rat = b[ekey]["Unrewarded"][ls:]
          n_hi += ls
          n_eng += e.sum()
          n_dis += d.sum()
          n_nan += (~e & ~d).sum()
          r_eng += list(rat[e])
          r_dis += list(rat[d])
          per.append((s, ls, int(e.sum()), int(d.sum()), round(b["engage_thr"][ekey], 3)))
      eng_counts[(ekey, L)] = (n_eng, n_dis, n_hi, np.nanmean(r_eng), np.nanmean(r_dis))
      tot = n_eng + n_dis
      within = {"engaged": abs(n_eng - 170) <= 17, "disengaged": abs(n_dis - 74) <= 7.4, "RE high": abs(n_hi - 90) <= 9}
      print(f"{L}: RE high {n_hi}, RE low {tot + n_nan}: engaged {n_eng} ({100 * n_eng / tot:.0f}% of classified), disengaged {n_dis}, undefined ratio {n_nan}; "
            f"ratio engaged {np.nanmean(r_eng):.3f} +/- {mean_ci(r_eng)[1]:.3f}, disengaged {np.nanmean(r_dis):.3f} +/- {mean_ci(r_dis)[1]:.3f}; counts within tolerance {within}")
      print("    per mouse (mouse, RE high laps, engaged, disengaged, threshold):", per)
=== engaged laps by the Methods ratio, traversal only (main)
L1: RE high 66, RE low 231: engaged 106 (46% of classified), disengaged 125, undefined ratio 0; ratio engaged 2.007 +/- 0.106, disengaged 1.362 +/- 0.061; counts within tolerance {'engaged': np.False_, 'disengaged': np.False_, 'RE high': False}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 12, 18, 2, 1.161), ('NR14', 10, 9, 4, 1.352), ('NR21', 5, 2, 25, 2.005), ('NR23', 2, 26, 4, 1.062), ('CFC4', 8, 5, 48, 2.169), ('NR32', 3, 1, 17, 1.781), ('NR34', 11, 4, 2, 1.142), ('CFC16', 2, 5, 19, 2.133), ('CFC17', 6, 27, 3, 1.309), ('CFC19', 7, 9, 1, 1.191)]
L2: RE high 76, RE low 221: engaged 100 (45% of classified), disengaged 121, undefined ratio 0; ratio engaged 1.989 +/- 0.109, disengaged 1.367 +/- 0.062; counts within tolerance {'engaged': np.False_, 'disengaged': np.False_, 'RE high': False}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 13, 18, 1, 1.161), ('NR14', 11, 8, 4, 1.352), ('NR21', 6, 2, 24, 2.005), ('NR23', 3, 26, 3, 1.062), ('CFC4', 9, 4, 48, 2.169), ('NR32', 4, 0, 17, 1.781), ('NR34', 12, 3, 2, 1.142), ('CFC16', 3, 5, 18, 2.133), ('CFC17', 7, 26, 3, 1.309), ('CFC19', 8, 8, 1, 1.191)]
L3: RE high 86, RE low 211: engaged 92 (44% of classified), disengaged 119, undefined ratio 0; ratio engaged 2.002 +/- 0.113, disengaged 1.362 +/- 0.062; counts within tolerance {'engaged': np.False_, 'disengaged': np.False_, 'RE high': True}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 14, 17, 1, 1.161), ('NR14', 12, 7, 4, 1.352), ('NR21', 7, 2, 23, 2.005), ('NR23', 4, 25, 3, 1.062), ('CFC4', 10, 3, 48, 2.169), ('NR32', 5, 0, 16, 1.781), ('NR34', 13, 2, 2, 1.142), ('CFC16', 4, 4, 18, 2.133), ('CFC17', 8, 25, 3, 1.309), ('CFC19', 9, 7, 1, 1.191)]
=== engaged laps by the Methods ratio, whole lap
L1: RE high 66, RE low 231: engaged 163 (96% of classified), disengaged 6, undefined ratio 62; ratio engaged 4.414 +/- 1.890, disengaged 1.407 +/- 0.176; counts within tolerance {'engaged': np.True_, 'disengaged': np.False_, 'RE high': False}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 12, 11, 0, -4.715), ('NR14', 10, 9, 0, -8.597), ('NR21', 5, 18, 6, 1.765), ('NR23', 2, 19, 0, 0.661), ('CFC4', 8, 40, 0, -6.829), ('NR32', 3, 15, 0, -11.04), ('NR34', 11, 3, 0, 0.567), ('CFC16', 2, 17, 0, -7.52), ('CFC17', 6, 21, 0, 0.815), ('CFC19', 7, 10, 0, 0.783)]
L2: RE high 76, RE low 221: engaged 157 (96% of classified), disengaged 6, undefined ratio 58; ratio engaged 4.456 +/- 1.963, disengaged 1.407 +/- 0.176; counts within tolerance {'engaged': np.True_, 'disengaged': np.False_, 'RE high': False}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 13, 10, 0, -4.715), ('NR14', 11, 9, 0, -8.597), ('NR21', 6, 17, 6, 1.765), ('NR23', 3, 19, 0, 0.661), ('CFC4', 9, 40, 0, -6.829), ('NR32', 4, 14, 0, -11.04), ('NR34', 12, 2, 0, 0.567), ('CFC16', 3, 16, 0, -7.52), ('CFC17', 7, 21, 0, 0.815), ('CFC19', 8, 9, 0, 0.783)]
L3: RE high 86, RE low 211: engaged 151 (96% of classified), disengaged 6, undefined ratio 54; ratio engaged 4.508 +/- 2.041, disengaged 1.407 +/- 0.176; counts within tolerance {'engaged': np.False_, 'disengaged': np.False_, 'RE high': True}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 14, 10, 0, -4.715), ('NR14', 12, 8, 0, -8.597), ('NR21', 7, 16, 6, 1.765), ('NR23', 4, 18, 0, 0.661), ('CFC4', 10, 39, 0, -6.829), ('NR32', 5, 13, 0, -11.04), ('NR34', 13, 2, 0, 0.567), ('CFC16', 4, 16, 0, -7.52), ('CFC17', 8, 21, 0, 0.815), ('CFC19', 9, 8, 0, 0.783)]
=== engaged laps by the code ratio, traversal only
L1: RE high 66, RE low 231: engaged 154 (67% of classified), disengaged 77, undefined ratio 0; ratio engaged 1.284 +/- 0.032, disengaged 1.029 +/- 0.034; counts within tolerance {'engaged': np.True_, 'disengaged': np.True_, 'RE high': False}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 12, 18, 2, 1.019), ('NR14', 10, 10, 3, 1.074), ('NR21', 5, 15, 12, 1.147), ('NR23', 2, 26, 4, 0.971), ('CFC4', 8, 23, 30, 1.259), ('NR32', 3, 4, 14, 1.176), ('NR34', 11, 4, 2, 0.802), ('CFC16', 2, 17, 7, 1.154), ('CFC17', 6, 28, 2, 1.042), ('CFC19', 7, 9, 1, 0.901)]
L2: RE high 76, RE low 221: engaged 148 (67% of classified), disengaged 73, undefined ratio 0; ratio engaged 1.287 +/- 0.032, disengaged 1.032 +/- 0.036; counts within tolerance {'engaged': np.False_, 'disengaged': np.True_, 'RE high': False}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 13, 18, 1, 1.019), ('NR14', 11, 9, 3, 1.074), ('NR21', 6, 15, 11, 1.147), ('NR23', 3, 26, 3, 0.971), ('CFC4', 9, 22, 30, 1.259), ('NR32', 4, 4, 13, 1.176), ('NR34', 12, 3, 2, 0.802), ('CFC16', 3, 16, 7, 1.154), ('CFC17', 7, 27, 2, 1.042), ('CFC19', 8, 8, 1, 0.901)]
L3: RE high 86, RE low 211: engaged 140 (66% of classified), disengaged 71, undefined ratio 0; ratio engaged 1.290 +/- 0.033, disengaged 1.030 +/- 0.036; counts within tolerance {'engaged': np.False_, 'disengaged': np.True_, 'RE high': True}
    per mouse (mouse, RE high laps, engaged, disengaged, threshold): [('NR6', 14, 17, 1, 1.019), ('NR14', 12, 8, 3, 1.074), ('NR21', 7, 15, 10, 1.147), ('NR23', 4, 25, 3, 0.971), ('CFC4', 10, 21, 30, 1.259), ('NR32', 5, 4, 12, 1.176), ('NR34', 13, 2, 2, 0.802), ('CFC16', 4, 15, 7, 1.154), ('CFC17', 8, 26, 2, 1.042), ('CFC19', 9, 7, 1, 0.901)]

Result: not reproduced. With engaged laps defined by the Methods ratio over the traversal (main reading), no RE low lap has an undefined ratio, and 106 of 231 RE low laps (46%) are engaged and 125 disengaged under L1 (100 and 121 under L2, 92 and 119 under L3), against 170 and 74; the RE high count is within tolerance only under L3 (86). The mean ratio of engaged laps is 2.007 and of disengaged laps 1.362, against 1.3 and 1.02. The code's ratio over the traversal, a variant, gives 154 engaged and 77 disengaged laps under L1 (67%), both within tolerance, with ratios of 1.284 and 1.029, but 66 RE high laps against 90 +/- 9. The whole-lap reading of the first version classified 62 of the 231 RE low laps as disengaged only because their ratio was undefined; with those laps left out it leaves 6 disengaged laps.

supp6b-decoder-engaged-laps¶

Decoder R2 is as low in engaged RE low laps (0.65) as in all RE low laps, while disengaged laps reduce it further (0.31).

In [18]:
for ekey, L in [(K.ENGAGE_MAIN, L) for L in K.LICK_READINGS] + [("ratios_code_trav", "L1")]:
    rows = []
    for s in WT:
        b = RES[s]["beh"]
        ls = b["lick_stop"][L]
        e, dmask = b["engaged_ur"][ekey], b["disengaged_ur"][ekey]
        u = lap_r2(s, "Unrewarded")
        eng = [v for k, v in u.items() if k >= ls and e[k]]
        dis = [v for k, v in u.items() if k >= ls and dmask[k]]
        rows.append({"mouse": s, "R": per_mouse_r2(s, L)["R"], "RE low": per_mouse_r2(s, L)["RE low"],
                     "engaged": np.nanmean(eng) if eng else np.nan, "disengaged": np.nanmean(dis) if dis else np.nan,
                     "n engaged": len(eng), "n disengaged": len(dis)})
    df = pd.DataFrame(rows).set_index("mouse")
    me, he = mean_ci(df["engaged"])
    md, hd = mean_ci(df["disengaged"])
    ok = df[["R", "engaged"]].dropna()
    w = stats.wilcoxon(ok["R"], ok["engaged"]) if len(ok) > 1 else None
    ok2 = df[["engaged", "disengaged"]].dropna()
    print(f"--- {RATIO_LABELS[ekey]}, {L}: engaged {me:.3f} [{me - he:.3f} {me + he:.3f}] (n={df['engaged'].notna().sum()}), disengaged {md:.3f} [{md - hd:.3f} {md + hd:.3f}] (n={df['disengaged'].notna().sum()}); "
          f"R vs engaged Wilcoxon p={w.pvalue if w else np.nan:.4f} (n={len(ok)}); mice with both: {len(ok2)}, engaged > disengaged in {(ok2['engaged'] > ok2['disengaged']).sum()}")
    print(df.round(3).to_string())
--- Methods ratio, traversal only (main), L1: engaged 0.759 [0.568 0.950] (n=10), disengaged 0.424 [0.214 0.634] (n=10); R vs engaged Wilcoxon p=0.0020 (n=10); mice with both: 10, engaged > disengaged in 9
           R  RE low  engaged  disengaged  n engaged  n disengaged
mouse                                                             
NR6    0.833   0.602    0.644       0.226         18             2
NR14   0.957   0.486    0.769      -0.152          9             4
NR21   0.990   0.721    0.943       0.703          2            25
NR23   0.970   0.765    0.792       0.593         26             4
CFC4   0.987   0.548    0.783       0.524          5            48
NR32   0.982   0.334    0.961       0.297          1            17
NR34   0.950   0.151    0.050       0.352          4             2
CFC16  0.956   0.808    0.928       0.777          5            19
CFC17  0.990   0.829    0.840       0.724         27             3
CFC19  0.986   0.814    0.882       0.197          9             1
--- Methods ratio, traversal only (main), L2: engaged 0.696 [0.415 0.978] (n=9), disengaged 0.448 [0.246 0.651] (n=10); R vs engaged Wilcoxon p=0.0039 (n=9); mice with both: 9, engaged > disengaged in 8
           R  RE low  engaged  disengaged  n engaged  n disengaged
mouse                                                             
NR6    0.833   0.639    0.644       0.560         18             1
NR14   0.957   0.455    0.758      -0.152          8             4
NR21   0.990   0.710    0.943       0.691          2            24
NR23   0.970   0.763    0.792       0.515         26             3
CFC4   0.987   0.540    0.734       0.524          4            48
NR32   0.982   0.297      NaN       0.297          0            17
NR34   0.950  -0.008   -0.247       0.352          3             2
CFC16  0.956   0.809    0.928       0.776          5            18
CFC17  0.990   0.829    0.841       0.724         26             3
CFC19  0.986   0.800    0.875       0.197          8             1
--- Methods ratio, traversal only (main), L3: engaged 0.658 [0.282 1.035] (n=9), disengaged 0.444 [0.241 0.647] (n=10); R vs engaged Wilcoxon p=0.0039 (n=9); mice with both: 9, engaged > disengaged in 8
           R  RE low  engaged  disengaged  n engaged  n disengaged
mouse                                                             
NR6    0.833   0.682    0.689       0.560         17             1
NR14   0.957   0.421    0.749      -0.152          7             4
NR21   0.990   0.699    0.943       0.678          2            23
NR23   0.970   0.771    0.801       0.515         25             3
CFC4   0.987   0.535    0.718       0.524          3            48
NR32   0.982   0.263      NaN       0.263          0            16
NR34   0.950  -0.136   -0.625       0.352          2             2
CFC16  0.956   0.809    0.955       0.776          4            18
CFC17  0.990   0.824    0.836       0.724         25             3
CFC19  0.986   0.777    0.860       0.197          7             1
--- code ratio, traversal only, L1: engaged 0.626 [0.399 0.854] (n=10), disengaged 0.473 [0.214 0.732] (n=10); R vs engaged Wilcoxon p=0.0020 (n=10); mice with both: 10, engaged > disengaged in 7
           R  RE low  engaged  disengaged  n engaged  n disengaged
mouse                                                             
NR6    0.833   0.602    0.638       0.278         18             2
NR14   0.957   0.486    0.722      -0.302         10             3
NR21   0.990   0.721    0.737       0.699         15            12
NR23   0.970   0.765    0.766       0.760         26             4
CFC4   0.987   0.548    0.664       0.460         23            30
NR32   0.982   0.334    0.273       0.352          4            14
NR34   0.950   0.151   -0.130       0.713          4             2
CFC16  0.956   0.808    0.890       0.610         17             7
CFC17  0.990   0.829    0.819       0.963         28             2
CFC19  0.986   0.814    0.882       0.197          9             1

Result: reproduced. With the main engagement split, under L1, engaged RE low laps decode at R2 0.759 [0.568 0.950] (paper 0.65 +/- 0.16) and disengaged laps at 0.424 [0.214 0.634] (paper 0.31 +/- 0.25), both within tolerance; engaged laps are below R (Wilcoxon p = 0.0020, 10 mice) and disengaged laps are below engaged in 9 of the 10 mice. Under L2 and L3 the means are 0.696 and 0.658 for engaged and 0.448 and 0.444 for disengaged laps, also within tolerance (R against engaged p = 0.0039, 9 mice). With the code's ratio (variant, L1) they are 0.626 and 0.473, engaged below R at p = 0.0020 and above disengaged in 7 of 10 mice. In some mice the engaged mean rests on 1 to 5 laps (NR21, NR32, CFC4, NR34, CFC16 under L1).

disengaged-laps-uniform¶

Disengaged laps are spread evenly through RE low; their probability in the first half of RE low is similar to the second half (P = 0.14) and their distribution does not differ from uniform (P = 0.7).

In [19]:
for ekey, L in [(K.ENGAGE_MAIN, L) for L in K.LICK_READINGS] + [("ratios_code_trav", "L1")]:
    f1, f2, posn = [], [], []
    for s in WT:
        b = RES[s]["beh"]
        ls = b["lick_stop"][L]
        cl = (b["engaged_ur"][ekey] | b["disengaged_ur"][ekey])[ls:]
        dis = b["disengaged_ur"][ekey][ls:][cl]  # laps with an undefined ratio left out
        n = len(dis)
        if n < 2:
            continue
        h = n // 2
        f1.append(dis[:h].mean())
        f2.append(dis[h:].mean())
        idx = np.where(dis)[0]
        posn += list((idx + 0.5) / n)
    f1, f2 = np.array(f1), np.array(f2)
    sw = stats.shapiro(f1 - f2).pvalue
    t = stats.ttest_rel(f1, f2)
    w = stats.wilcoxon(f1, f2)
    ks = stats.kstest(posn, "uniform")
    print(f"{RATIO_LABELS[ekey]}, {L}: fraction disengaged first half {f1.mean():.3f}, second half {f2.mean():.3f} (n = {len(f1)} mice); Shapiro-Wilk p={sw:.3f}; "
          f"paired t p={t.pvalue:.3f}; Wilcoxon p={w.pvalue:.3f}; KS against uniform on {len(posn)} laps D={ks.statistic:.3f} p={ks.pvalue:.3f}")
Methods ratio, traversal only (main), L1: fraction disengaged first half 0.383, second half 0.541 (n = 10 mice); Shapiro-Wilk p=0.086; paired t p=0.094; Wilcoxon p=0.094; KS against uniform on 125 laps D=0.053 p=0.853
Methods ratio, traversal only (main), L2: fraction disengaged first half 0.381, second half 0.550 (n = 10 mice); Shapiro-Wilk p=0.007; paired t p=0.089; Wilcoxon p=0.078; KS against uniform on 121 laps D=0.053 p=0.864
Methods ratio, traversal only (main), L3: fraction disengaged first half 0.443, second half 0.539 (n = 10 mice); Shapiro-Wilk p=0.058; paired t p=0.236; Wilcoxon p=0.312; KS against uniform on 119 laps D=0.043 p=0.975
code ratio, traversal only, L1: fraction disengaged first half 0.284, second half 0.324 (n = 10 mice); Shapiro-Wilk p=0.053; paired t p=0.639; Wilcoxon p=1.000; KS against uniform on 77 laps D=0.089 p=0.541

Result: reproduced. With the main engagement split, the fraction of disengaged laps is 0.383 in the first and 0.541 in the second half of RE low under L1 (paired t p = 0.094; Wilcoxon p = 0.094; 10 mice), and the positions of the 125 disengaged laps do not differ from uniform (KS p = 0.853); L2 and L3 give p from 0.078 to 0.975. Both tests give p >= 0.05 under every reading, as the criterion asks, though the halves differ by 0.16 and 0.17 under L1 and L2. The code's ratio (variant, L1) gives 0.284 and 0.324 (paired t p = 0.639, KS p = 0.541).

Place fields: the mice of Figures 2 and 3¶

Figures 2 and 3 use the R-UR-RR mice with more than 12 engaged RE low laps (Methods, Engagement with VR, approach behavior). Place fields are defined separately in R (all laps), in the engaged RE low laps and in RR (all laps), by the criteria in the helper's docstring. With the main engagement split (Methods ratio over the traversal) 2 of the 5 R-UR-RR mice qualify under every lick stop reading (NR6 and NR23), against 3 of 6 in the paper; with the code's ratio (reading "L1c", a variant: lick stop L1, engaged laps by the code's ratio) 4 qualify (NR6, NR21, NR23, CFC4). Every Figure 2 and 3 analysis below is shown under L1 to L3 and L1c; the outcomes are decided on L1 to L3.

In [20]:
# Readings of the lap sets: L1 to L3 with engaged laps by the main ratio; "L1c" (variant) = lick stop L1
# with engaged laps by the code's ratio over the traversal
READS = K.LICK_READINGS + ["L1c"]

def lsr(L):
    return "L1" if L == "L1c" else L

def eng_key(L):
    return "ratios_code_trav" if L == "L1c" else K.ENGAGE_MAIN

def low_name(L):
    return "RElow_engc_L1" if L == "L1c" else f"RElow_eng_{L}"

def eng_laps(s, L):
    b = RES[s]["beh"]
    e = b["engaged_ur"][eng_key(L)]
    return [i for i in range(b["lick_stop"][lsr(L)], len(e)) if e[i]]

FIG23 = {}
for L in READS:
    counts = {s: len(eng_laps(s, L)) for s in K.WT_FULL}
    FIG23[L] = [s for s in K.WT_FULL if counts[s] > 12]
    print(f"{L}: engaged RE low laps per R-UR-RR mouse {counts}; mice with more than 12: {FIG23[L]}")
print("place fields per mouse and lap set:")
print(pd.DataFrame({s: {k: (len(v["fields"]) if v else None) for k, v in RES[s]["fields"].items()} for s in WT}).to_string())

def fields_df(s, name):
    f = RES[s]["fields"].get(name)
    if not f:
        return pd.DataFrame(columns=["cell", "start", "end", "reliability", "out_in", "width", "intensity", "com", "frac_laps"])
    d = pd.DataFrame(f["fields"])
    d["mouse"] = s
    return d

def cell_com(df):
    """COM of each cell's strongest field."""
    return df.sort_values("intensity").groupby("cell")["com"].last()

def mean_map(s, cond, cells, laps=None):
    m = RES[s]["maps"][cond][cells]
    if laps is not None:
        m = m[:, laps]
    return m.mean(axis=1)

def rowcorr(a, b):
    a = a - a.mean(1, keepdims=True)
    b = b - b.mean(1, keepdims=True)
    den = np.sqrt((a ** 2).sum(1) * (b ** 2).sum(1))
    with np.errstate(invalid="ignore", divide="ignore"):
        return (a * b).sum(1) / den

def group_of(com):
    return np.where(com < 50, "start", np.where(com < 150, "middle", "end"))
L1: engaged RE low laps per R-UR-RR mouse {'NR6': 18, 'NR14': 9, 'NR21': 2, 'NR23': 26, 'CFC4': 5}; mice with more than 12: ['NR6', 'NR23']
L2: engaged RE low laps per R-UR-RR mouse {'NR6': 18, 'NR14': 8, 'NR21': 2, 'NR23': 26, 'CFC4': 4}; mice with more than 12: ['NR6', 'NR23']
L3: engaged RE low laps per R-UR-RR mouse {'NR6': 17, 'NR14': 7, 'NR21': 2, 'NR23': 25, 'CFC4': 3}; mice with more than 12: ['NR6', 'NR23']
L1c: engaged RE low laps per R-UR-RR mouse {'NR6': 18, 'NR14': 10, 'NR21': 15, 'NR23': 26, 'CFC4': 23}; mice with more than 12: ['NR6', 'NR21', 'NR23', 'CFC4']
place fields per mouse and lap set:
               NR6  NR14   NR21  NR23  CFC4   NR32   NR34  CFC16  CFC17  CFC19
R              139    97  203.0   106   299  139.0  265.0  195.0  155.0   76.0
RElow_eng_L1    41    35    NaN   110    17    NaN   30.0   11.0  148.0  100.0
RElow_all_L1    53    28  140.0   102   132   75.0   29.0  155.0  160.0   71.0
RElow_eng_L2    39    20    NaN   110    12    NaN    1.0   12.0  130.0   70.0
RElow_all_L2    52    31  136.0   101   130   69.0   17.0  149.0  137.0   62.0
RElow_eng_L3    44    21    NaN   102     9    NaN    NaN    8.0  129.0   35.0
RElow_all_L3    50    24  142.0   102   122   62.0   15.0  145.0  139.0   40.0
RR             106    85  138.0   148   344    NaN    NaN    NaN    NaN    NaN
RElow_engc_L1   50    35   72.0   102   108    NaN    NaN    NaN    NaN    NaN
N               71    72   79.0   123   247    NaN    NaN    NaN    NaN    NaN
N_early          5    32   34.0    22    61    NaN    NaN    NaN    NaN    NaN
N_late          12    32   60.0    35    62    NaN    NaN    NaN    NaN    NaN

fig2c-spatial-correlation-drop¶

Place fields defined in R partially remap in RE low (engaged laps), with a lower spatial correlation between R and RE low than within R, and remap less in RR.

Reading S1 (within-R from control mice) needs the control mice, which the dandiset does not hold; reading S2 (first against second half of the R laps of the same mice) is run. The criterion requires S1 and S2 to agree, so the claim cannot be fully reproduced here whatever S2 gives.

In [21]:
def spatial_corr(L, mice):
    rows = []
    for s in mice:
        dfR = fields_df(s, "R")
        cells = np.array(sorted(dfR["cell"].unique()))
        nR = RES[s]["maps"]["Rewarded"].shape[1]
        mR = mean_map(s, "Rewarded", cells)
        h1 = mean_map(s, "Rewarded", cells, list(range(nR // 2)))
        h2 = mean_map(s, "Rewarded", cells, list(range(nR // 2, nR)))
        b = RES[s]["beh"]
        low = eng_laps(s, L)
        mlow = mean_map(s, "Unrewarded", cells, low)
        d = pd.DataFrame({"mouse": s, "cell": cells, "com": cell_com(dfR).reindex(cells).values,
                          "within R": rowcorr(h1, h2), "R vs RE low": rowcorr(mR, mlow)})
        if "ReRewarded" in RES[s]["maps"]:
            d["R vs RR"] = rowcorr(mR, mean_map(s, "ReRewarded", cells))
        rows.append(d)
    return pd.concat(rows, ignore_index=True)

fig2c = {}
for L in READS:
    sc = spatial_corr(L, FIG23[L])
    fig2c[L] = sc
    ok = sc[["within R", "R vs RE low", "R vs RR"]].dropna()
    t1 = stats.ttest_rel(ok["R vs RE low"], ok["within R"])
    t2 = stats.ttest_rel(ok["R vs RR"], ok["R vs RE low"])
    t3 = stats.ttest_rel(ok["R vs RR"], ok["within R"])
    print(f"--- {L}, mice {FIG23[L]}: {len(sc)} R place cells, {len(ok)} with all three correlations defined ({len(sc) - len(ok)} dropped: no activity in a lap set)")
    print(f"    mean within R {ok['within R'].mean():.3f}, R vs RE low {ok['R vs RE low'].mean():.3f}, R vs RR {ok['R vs RR'].mean():.3f}")
    print(f"    RE low vs within R t={t1.statistic:.2f} p={t1.pvalue:.2e}; RR vs RE low t={t2.statistic:.2f} p={t2.pvalue:.2e}; RR vs within R t={t3.statistic:.2f} p={t3.pvalue:.2e}")
    print("    per mouse means:\n", ok.join(sc["mouse"]).groupby("mouse").mean(numeric_only=True).round(3).to_string())
--- L1, mice ['NR6', 'NR23']: 240 R place cells, 238 with all three correlations defined (2 dropped: no activity in a lap set)
    mean within R 0.799, R vs RE low 0.503, R vs RR 0.579
    RE low vs within R t=-12.73 p=1.24e-28; RR vs RE low t=3.38 p=8.45e-04; RR vs within R t=-9.73 p=4.88e-19
    per mouse means:
        within R  R vs RE low  R vs RR
mouse                                
NR23      0.812        0.578    0.620
NR6       0.788        0.444    0.546
--- L2, mice ['NR6', 'NR23']: 240 R place cells, 238 with all three correlations defined (2 dropped: no activity in a lap set)
    mean within R 0.799, R vs RE low 0.503, R vs RR 0.579
    RE low vs within R t=-12.73 p=1.24e-28; RR vs RE low t=3.38 p=8.45e-04; RR vs within R t=-9.73 p=4.88e-19
    per mouse means:
        within R  R vs RE low  R vs RR
mouse                                
NR23      0.812        0.578    0.620
NR6       0.788        0.444    0.546
--- L3, mice ['NR6', 'NR23']: 240 R place cells, 238 with all three correlations defined (2 dropped: no activity in a lap set)
    mean within R 0.799, R vs RE low 0.506, R vs RR 0.579
    RE low vs within R t=-12.48 p=8.55e-28; RR vs RE low t=3.21 p=1.51e-03; RR vs within R t=-9.73 p=4.88e-19
    per mouse means:
        within R  R vs RE low  R vs RR
mouse                                
NR23      0.812        0.579    0.620
NR6       0.788        0.449    0.546
--- L1c, mice ['NR6', 'NR21', 'NR23', 'CFC4']: 741 R place cells, 737 with all three correlations defined (4 dropped: no activity in a lap set)
    mean within R 0.857, R vs RE low 0.556, R vs RR 0.678
    RE low vs within R t=-22.17 p=8.03e-84; RR vs RE low t=8.78 p=1.12e-17; RR vs within R t=-14.26 p=6.15e-41
    per mouse means:
        within R  R vs RE low  R vs RR
mouse                                
CFC4      0.873        0.560    0.799
NR21      0.901        0.625    0.617
NR23      0.812        0.566    0.620
NR6       0.788        0.436    0.546

Result: partially reproduced. Under reading S2, for the 238 R place cells of the 2 qualifying mice with correlations in every lap set (L1), the correlation of R with RE low (0.503) is below the within-R correlation (0.799; paired t p = 1.2e-28), and R with RR (0.579) lies between them, above RE low (p = 8.5e-04); L2 and L3 give the same, and so does the L1c variant with 4 mice (0.556 against 0.857, RR 0.678). The criterion requires readings S1 and S2 to agree; S1 needs the control mice, which are not in the dandiset, so only part of the test can be run and the claim is partially reproduced.

fig2d-remapping-all-locations¶

The drop in spatial correlation in RE low occurs at all track locations, not only near the reward site.

In [22]:
fig2d = {}
for L in READS:
    sc = fig2c[L].dropna(subset=["within R", "R vs RE low"])
    res_g = {}
    for g in ["start", "middle", "end"]:
        d = sc[group_of(sc["com"].values) == g]
        t = stats.ttest_rel(d["R vs RE low"], d["within R"])
        res_g[g] = (len(d), d["within R"].mean(), d["R vs RE low"].mean(), t.statistic, t.pvalue)
        print(f"{L} {g:6s}: n={len(d)} cells, within R {d['within R'].mean():.3f}, R vs RE low {d['R vs RE low'].mean():.3f}, paired t={t.statistic:.2f} p={t.pvalue:.2e}")
    fig2d[L] = res_g
    print(f"{L}: groups with RE low lower at p < 0.01: {sum(v[2] < v[1] and v[4] < 0.01 for v in res_g.values())} of 3")
L1 start : n=53 cells, within R 0.820, R vs RE low 0.514, paired t=-5.92 p=2.54e-07
L1 middle: n=124 cells, within R 0.801, R vs RE low 0.478, paired t=-9.81 p=3.93e-17
L1 end   : n=62 cells, within R 0.777, R vs RE low 0.540, paired t=-5.80 p=2.57e-07
L1: groups with RE low lower at p < 0.01: 3 of 3
L2 start : n=53 cells, within R 0.820, R vs RE low 0.514, paired t=-5.92 p=2.54e-07
L2 middle: n=124 cells, within R 0.801, R vs RE low 0.478, paired t=-9.81 p=3.93e-17
L2 end   : n=62 cells, within R 0.777, R vs RE low 0.540, paired t=-5.80 p=2.57e-07
L2: groups with RE low lower at p < 0.01: 3 of 3
L3 start : n=53 cells, within R 0.820, R vs RE low 0.509, paired t=-6.02 p=1.81e-07
L3 middle: n=124 cells, within R 0.801, R vs RE low 0.482, paired t=-9.58 p=1.44e-16
L3 end   : n=62 cells, within R 0.777, R vs RE low 0.548, paired t=-5.53 p=7.07e-07
L3: groups with RE low lower at p < 0.01: 3 of 3
L1c start : n=169 cells, within R 0.865, R vs RE low 0.517, paired t=-11.65 p=2.33e-23
L1c middle: n=327 cells, within R 0.873, R vs RE low 0.590, paired t=-14.25 p=3.68e-36
L1c end   : n=242 cells, within R 0.830, R vs RE low 0.538, paired t=-12.50 p=5.53e-28
L1c: groups with RE low lower at p < 0.01: 3 of 3

Result: partially reproduced. Under reading S2 the R against RE low correlation is below within-R at the start (0.514 against 0.820, p = 2.5e-07), middle (0.478 against 0.801, p = 3.9e-17) and end (0.540 against 0.777, p = 2.6e-07) under L1, 3 of 3 groups under every lick stop reading and under L1c. Reading S1, which the criterion requires to agree, cannot be run (no control mice), so the claim is partially reproduced.

fig3a-field-numbers-similar and the parameters of Figure 3b¶

The total number of place fields tiling the track is similar in R, RE low (engaged laps) and RR (605, 663 and 674 fields in 3 mice).

In [23]:
PF = {}
for L in READS:
    PF[L] = {"R": pd.concat([fields_df(s, "R") for s in FIG23[L]], ignore_index=True),
             "RE low": pd.concat([fields_df(s, low_name(L)) for s in FIG23[L]], ignore_index=True),
             "RR": pd.concat([fields_df(s, "RR") for s in FIG23[L]], ignore_index=True)}
    n = {k: len(v) for k, v in PF[L].items()}
    within = {k: abs(n[k] - v) <= tol for k, (v, tol) in {"R": (605, 60), "RE low": (663, 66), "RR": (674, 67)}.items()}
    print(f"{L}, mice {FIG23[L]}: fields R {n['R']}, RE low engaged {n['RE low']}, RR {n['RR']}; within tolerance {within}")
    print("    per mouse:", {k: v.groupby("mouse").size().to_dict() for k, v in PF[L].items()})
    print("    place cells (cells with a field) per condition:", {k: v.groupby("mouse")["cell"].nunique().sum() for k, v in PF[L].items()})
L1, mice ['NR6', 'NR23']: fields R 245, RE low engaged 151, RR 254; within tolerance {'R': False, 'RE low': False, 'RR': False}
    per mouse: {'R': {'NR23': 106, 'NR6': 139}, 'RE low': {'NR23': 110, 'NR6': 41}, 'RR': {'NR23': 148, 'NR6': 106}}
    place cells (cells with a field) per condition: {'R': np.int64(240), 'RE low': np.int64(149), 'RR': np.int64(251)}
L2, mice ['NR6', 'NR23']: fields R 245, RE low engaged 149, RR 254; within tolerance {'R': False, 'RE low': False, 'RR': False}
    per mouse: {'R': {'NR23': 106, 'NR6': 139}, 'RE low': {'NR23': 110, 'NR6': 39}, 'RR': {'NR23': 148, 'NR6': 106}}
    place cells (cells with a field) per condition: {'R': np.int64(240), 'RE low': np.int64(146), 'RR': np.int64(251)}
L3, mice ['NR6', 'NR23']: fields R 245, RE low engaged 146, RR 254; within tolerance {'R': False, 'RE low': False, 'RR': False}
    per mouse: {'R': {'NR23': 106, 'NR6': 139}, 'RE low': {'NR23': 102, 'NR6': 44}, 'RR': {'NR23': 148, 'NR6': 106}}
    place cells (cells with a field) per condition: {'R': np.int64(240), 'RE low': np.int64(141), 'RR': np.int64(251)}
L1c, mice ['NR6', 'NR21', 'NR23', 'CFC4']: fields R 747, RE low engaged 332, RR 736; within tolerance {'R': False, 'RE low': False, 'RR': True}
    per mouse: {'R': {'CFC4': 299, 'NR21': 203, 'NR23': 106, 'NR6': 139}, 'RE low': {'CFC4': 108, 'NR21': 72, 'NR23': 102, 'NR6': 50}, 'RR': {'CFC4': 344, 'NR21': 138, 'NR23': 148, 'NR6': 106}}
    place cells (cells with a field) per condition: {'R': np.int64(741), 'RE low': np.int64(329), 'RR': np.int64(732)}

Result: not reproduced. In the 2 qualifying mice, R has 245 fields, the engaged RE low laps 151 (L1; 149 and 146 under L2 and L3) and RR 254, so the RE low count is 38% below R and the numbers are not similar; all three are far from 605, 663 and 674, which the paper counts in 3 mice. With 4 mice (L1c) the counts are 747, 332 and 736: RR is within tolerance and RE low again far below R.

fig3b-reliability, fig3b-out-in-ratio, fig3b-width-intensity¶

Place field trial to trial reliability falls from 0.47 in R to 0.32 in RE low (engaged laps) and returns towards R in RR (0.41). The out/in place field firing ratio rises from 0.10 in R to 0.16 in RE low and returns towards R in RR (0.12). Place fields widen from 53.34 cm in R to 60.56 cm in RE low (54.08 in RR), and firing intensity falls slightly from 0.36 to 0.31 and stays low in RR (0.29).

In [24]:
EXP3B = {"reliability": {"R": (0.47, 0.10), "RE low": (0.32, 0.17), "RR": (0.41, 0.10)},
         "out_in": {"R": (0.10, 0.03), "RE low": (0.16, 0.04), "RR": (0.12, 0.04)},
         "width": {"R": (53.34, 9.47), "RE low": (60.56, 12.5), "RR": (54.08, 7.23)},
         "intensity": {"R": (0.36, 0.05), "RE low": (0.31, 0.09), "RR": (0.29, 0.05)}}
fig3b = {}
for L in READS:
    print(f"--- {L}")
    for p, ex in EXP3B.items():
        for reading in ["per-mouse means", "pooled fields"]:
            vals = {}
            for c in ["R", "RE low", "RR"]:
                d = PF[L][c]
                if reading == "per-mouse means":
                    pm = d.groupby("mouse")[p].mean()
                    vals[c] = mean_ci(pm)
                else:
                    vals[c] = mean_ci(d[p])
            within = {c: abs(vals[c][0] - ex[c][0]) <= ex[c][1] for c in ex}
            fig3b[(L, p, reading)] = (vals, within)
            print(f"  {p:11s} {reading:16s}: " + "; ".join(f"{c} {m:.3f} +/- {h:.3f}" for c, (m, h) in vals.items()) + f"  within tolerance {within}")
        a, b2 = PF[L]["R"][p].dropna(), PF[L]["RE low"][p].dropna()
        t = stats.ttest_ind(a, b2)
        ks = stats.ks_2samp(a, b2)
        print(f"  {p:11s} R vs RE low pooled fields: unpaired t={t.statistic:.2f} p={t.pvalue:.2e}; KS D={ks.statistic:.3f} p={ks.pvalue:.2e}; range R {a.min():.2f} to {a.max():.2f}, RE low {b2.min():.2f} to {b2.max():.2f}")
print("per mouse (L1):")
print(pd.concat({c: PF["L1"][c].groupby("mouse")[["reliability", "out_in", "width", "intensity"]].mean() for c in PF["L1"]}).round(3).to_string())
# width variant: the extent of the candidate field (bins above 25% of the way from baseline to peak), which does not use the
# "mean above 0" extent of the field-only map
for L in READS:
    print(f"{L} candidate-field width, per-mouse means (cm):", {c: round(((PF[L][c]["end"] - PF[L][c]["start"]) * K.BIN).groupby(PF[L][c]["mouse"]).mean().mean(), 1) for c in PF[L]})
--- L1
  reliability per-mouse means : R 0.545 +/- 0.858; RE low 0.381 +/- 0.847; RR 0.495 +/- 1.297  within tolerance {'R': True, 'RE low': True, 'RR': True}
  reliability pooled fields   : R 0.554 +/- 0.023; RE low 0.351 +/- 0.024; RR 0.478 +/- 0.023  within tolerance {'R': True, 'RE low': True, 'RR': True}
  reliability R vs RE low pooled fields: unpaired t=11.46 p=2.03e-26; KS D=0.483 p=2.37e-20; range R 0.21 to 0.91, RE low 0.08 to 0.78
  out_in      per-mouse means : R 0.068 +/- 0.029; RE low 0.086 +/- 0.035; RR 0.075 +/- 0.092  within tolerance {'R': False, 'RE low': False, 'RR': False}
  out_in      pooled fields   : R 0.068 +/- 0.007; RE low 0.087 +/- 0.009; RR 0.076 +/- 0.007  within tolerance {'R': False, 'RE low': False, 'RR': False}
  out_in      R vs RE low pooled fields: unpaired t=-3.21 p=1.44e-03; KS D=0.192 p=1.65e-03; range R 0.00 to 0.25, RE low 0.01 to 0.24
  width       per-mouse means : R 70.132 +/- 106.803; RE low 65.597 +/- 204.377; RR 73.103 +/- 147.306  within tolerance {'R': False, 'RE low': True, 'RR': False}
  width       pooled fields   : R 69.000 +/- 3.914; RE low 72.947 +/- 5.436; RR 75.020 +/- 4.495  within tolerance {'R': False, 'RE low': True, 'RR': False}
  width       R vs RE low pooled fields: unpaired t=-1.19 p=2.36e-01; KS D=0.123 p=1.07e-01; range R 10.00 to 200.00, RE low 10.00 to 180.00
  intensity   per-mouse means : R 0.380 +/- 0.981; RE low 0.311 +/- 0.028; RR 0.393 +/- 0.325  within tolerance {'R': True, 'RE low': True, 'RR': False}
  intensity   pooled fields   : R 0.391 +/- 0.033; RE low 0.310 +/- 0.029; RR 0.389 +/- 0.027  within tolerance {'R': True, 'RE low': True, 'RR': False}
  intensity   R vs RE low pooled fields: unpaired t=3.36 p=8.69e-04; KS D=0.163 p=1.23e-02; range R 0.11 to 1.85, RE low 0.13 to 1.35
--- L2
  reliability per-mouse means : R 0.545 +/- 0.858; RE low 0.393 +/- 0.929; RR 0.495 +/- 1.297  within tolerance {'R': True, 'RE low': True, 'RR': True}
  reliability pooled fields   : R 0.554 +/- 0.023; RE low 0.358 +/- 0.025; RR 0.478 +/- 0.023  within tolerance {'R': True, 'RE low': True, 'RR': True}
  reliability R vs RE low pooled fields: unpaired t=10.87 p=3.17e-24; KS D=0.454 p=9.54e-18; range R 0.21 to 0.91, RE low 0.08 to 0.82
  out_in      per-mouse means : R 0.068 +/- 0.029; RE low 0.087 +/- 0.054; RR 0.075 +/- 0.092  within tolerance {'R': False, 'RE low': False, 'RR': False}
  out_in      pooled fields   : R 0.068 +/- 0.007; RE low 0.089 +/- 0.010; RR 0.076 +/- 0.007  within tolerance {'R': False, 'RE low': False, 'RR': False}
  out_in      R vs RE low pooled fields: unpaired t=-3.56 p=4.10e-04; KS D=0.199 p=1.04e-03; range R 0.00 to 0.25, RE low 0.01 to 0.24
  width       per-mouse means : R 70.132 +/- 106.803; RE low 64.595 +/- 187.076; RR 73.103 +/- 147.306  within tolerance {'R': False, 'RE low': True, 'RR': False}
  width       pooled fields   : R 69.000 +/- 3.914; RE low 71.611 +/- 5.195; RR 75.020 +/- 4.495  within tolerance {'R': False, 'RE low': True, 'RR': False}
  width       R vs RE low pooled fields: unpaired t=-0.80 p=4.25e-01; KS D=0.108 p=2.11e-01; range R 10.00 to 200.00, RE low 10.00 to 170.00
  intensity   per-mouse means : R 0.380 +/- 0.981; RE low 0.312 +/- 0.124; RR 0.393 +/- 0.325  within tolerance {'R': True, 'RE low': True, 'RR': False}
  intensity   pooled fields   : R 0.391 +/- 0.033; RE low 0.308 +/- 0.029; RR 0.389 +/- 0.027  within tolerance {'R': True, 'RE low': True, 'RR': False}
  intensity   R vs RE low pooled fields: unpaired t=3.41 p=7.07e-04; KS D=0.186 p=2.79e-03; range R 0.11 to 1.85, RE low 0.13 to 1.35
--- L3
  reliability per-mouse means : R 0.545 +/- 0.858; RE low 0.360 +/- 0.542; RR 0.495 +/- 1.297  within tolerance {'R': True, 'RE low': True, 'RR': True}
  reliability pooled fields   : R 0.554 +/- 0.023; RE low 0.343 +/- 0.023; RR 0.478 +/- 0.023  within tolerance {'R': True, 'RE low': True, 'RR': True}
  reliability R vs RE low pooled fields: unpaired t=11.94 p=3.26e-28; KS D=0.483 p=6.71e-20; range R 0.21 to 0.91, RE low 0.09 to 0.78
  out_in      per-mouse means : R 0.068 +/- 0.029; RE low 0.092 +/- 0.020; RR 0.075 +/- 0.092  within tolerance {'R': False, 'RE low': False, 'RR': False}
  out_in      pooled fields   : R 0.068 +/- 0.007; RE low 0.092 +/- 0.010; RR 0.076 +/- 0.007  within tolerance {'R': False, 'RE low': False, 'RR': False}
  out_in      R vs RE low pooled fields: unpaired t=-3.88 p=1.21e-04; KS D=0.208 p=5.96e-04; range R 0.00 to 0.25, RE low 0.00 to 0.25
  width       per-mouse means : R 70.132 +/- 106.803; RE low 61.052 +/- 195.293; RR 73.103 +/- 147.306  within tolerance {'R': False, 'RE low': True, 'RR': False}
  width       pooled fields   : R 69.000 +/- 3.914; RE low 67.158 +/- 5.448; RR 75.020 +/- 4.495  within tolerance {'R': False, 'RE low': True, 'RR': False}
  width       R vs RE low pooled fields: unpaired t=0.55 p=5.81e-01; KS D=0.140 p=4.95e-02; range R 10.00 to 200.00, RE low 10.00 to 170.00
  intensity   per-mouse means : R 0.380 +/- 0.981; RE low 0.280 +/- 0.080; RR 0.393 +/- 0.325  within tolerance {'R': True, 'RE low': True, 'RR': False}
  intensity   pooled fields   : R 0.391 +/- 0.033; RE low 0.277 +/- 0.027; RR 0.389 +/- 0.027  within tolerance {'R': True, 'RE low': True, 'RR': False}
  intensity   R vs RE low pooled fields: unpaired t=4.74 p=3.00e-06; KS D=0.267 p=3.04e-06; range R 0.11 to 1.85, RE low 0.10 to 1.41
--- L1c
  reliability per-mouse means : R 0.570 +/- 0.143; RE low 0.375 +/- 0.163; RR 0.520 +/- 0.149  within tolerance {'R': False, 'RE low': True, 'RR': False}
  reliability pooled fields   : R 0.572 +/- 0.014; RE low 0.351 +/- 0.017; RR 0.511 +/- 0.014  within tolerance {'R': False, 'RE low': True, 'RR': False}
  reliability R vs RE low pooled fields: unpaired t=17.89 p=7.43e-63; KS D=0.449 p=2.79e-42; range R 0.14 to 0.96, RE low 0.02 to 0.82
  out_in      per-mouse means : R 0.074 +/- 0.018; RE low 0.105 +/- 0.016; RR 0.080 +/- 0.029  within tolerance {'R': True, 'RE low': False, 'RR': True}
  out_in      pooled fields   : R 0.075 +/- 0.004; RE low 0.105 +/- 0.007; RR 0.077 +/- 0.004  within tolerance {'R': True, 'RE low': False, 'RR': False}
  out_in      R vs RE low pooled fields: unpaired t=-7.94 p=5.24e-15; KS D=0.249 p=4.90e-13; range R 0.00 to 0.25, RE low 0.01 to 0.25
  width       per-mouse means : R 85.792 +/- 30.780; RE low 82.074 +/- 40.818; RR 79.032 +/- 21.209  within tolerance {'R': False, 'RE low': False, 'RR': False}
  width       pooled fields   : R 90.850 +/- 3.394; RE low 87.620 +/- 4.796; RR 83.730 +/- 3.118  within tolerance {'R': False, 'RE low': False, 'RR': False}
  width       R vs RE low pooled fields: unpaired t=1.06 p=2.92e-01; KS D=0.066 p=2.59e-01; range R 10.00 to 200.00, RE low 15.00 to 200.00
  intensity   per-mouse means : R 0.411 +/- 0.115; RE low 0.316 +/- 0.050; RR 0.384 +/- 0.050  within tolerance {'R': False, 'RE low': True, 'RR': False}
  intensity   pooled fields   : R 0.423 +/- 0.023; RE low 0.313 +/- 0.020; RR 0.387 +/- 0.017  within tolerance {'R': False, 'RE low': True, 'RR': False}
  intensity   R vs RE low pooled fields: unpaired t=5.96 p=3.36e-09; KS D=0.179 p=6.22e-07; range R 0.11 to 3.29, RE low 0.12 to 1.27
per mouse (L1):
              reliability  out_in   width  intensity
       mouse                                        
R      NR23         0.477   0.066  78.538      0.303
       NR6          0.612   0.070  61.727      0.458
RE low NR23         0.315   0.088  81.682      0.309
       NR6          0.448   0.083  49.512      0.313
RR     NR23         0.393   0.082  84.696      0.368
       NR6          0.597   0.068  61.509      0.419
L1 candidate-field width, per-mouse means (cm): {'R': np.float64(33.6), 'RE low': np.float64(34.7), 'RR': np.float64(35.3)}
L2 candidate-field width, per-mouse means (cm): {'R': np.float64(33.6), 'RE low': np.float64(34.1), 'RR': np.float64(35.3)}
L3 candidate-field width, per-mouse means (cm): {'R': np.float64(33.6), 'RE low': np.float64(31.4), 'RR': np.float64(35.3)}
L1c candidate-field width, per-mouse means (cm): {'R': np.float64(40.9), 'RE low': np.float64(43.2), 'RR': np.float64(39.5)}

Result: reproduced. Over the per-mouse means of the 2 qualifying mice, reliability is 0.545 in R, 0.381 in RE low and 0.495 in RR (L1), within the tolerances of 0.47 +/- 0.10, 0.32 +/- 0.17 and 0.41 +/- 0.10, and RE low is below R (pooled fields, unpaired t p = 2.0e-26; KS p = 2.4e-20); the pooled fields give 0.554, 0.351 and 0.478, and L2 and L3 also hold. With 4 mice (L1c) R (0.570) and RR (0.520) are just outside their tolerances.

Result: not reproduced. The out/in ratio rises from R to RE low (pooled fields, unpaired t p = 1.4e-03 under L1), but the per-mouse means are R 0.068, RE low 0.086 and RR 0.075 under L1 (0.087 and 0.092 for RE low under L2 and L3), against 0.10 +/- 0.03, 0.16 +/- 0.04 and 0.12 +/- 0.04: R is just below its band and RE low well below it under every reading, per mouse or pooled. With 4 mice (L1c) R (0.074) and RR (0.080) are within and RE low (0.105) is not. The first version passed under L2 only, with the RE low laps of the old engagement split.

Result: not reproduced. Fields are wider than the paper's: per-mouse means of 70.1 cm in R, 65.6 cm in RE low and 73.1 cm in RR (L1), against 53.34, 60.56 and 54.08 cm; only RE low is within tolerance, and RE low is not wider than R (pooled p = 0.24). Firing intensity is within tolerance in R (0.380) and RE low (0.311) and falls from R to RE low (pooled p = 8.7e-04), but RR (0.393) is above 0.29 +/- 0.05. The candidate regions that the field search cuts (bins above 25% of the way from baseline to peak) are 31 to 35 cm wide on average; the width rule of the Methods, from where the field-only mean map rises above 0 to where it returns to 0, roughly doubles that.

fig3c-degradation-all-locations¶

The degradation of place fields in RE low (reliability and out/in ratio) is seen at all track locations.

In [25]:
for L in READS:
    n_ok = {}
    for p, direction in [("reliability", -1), ("out_in", 1)]:
        k = 0
        for g in ["start", "middle", "end"]:
            a = PF[L]["R"][group_of(PF[L]["R"]["com"].values) == g][p].dropna()
            b2 = PF[L]["RE low"][group_of(PF[L]["RE low"]["com"].values) == g][p].dropna()
            ks = stats.ks_2samp(a, b2)
            ok = (np.sign(b2.mean() - a.mean()) == direction) and ks.pvalue < 0.01
            k += ok
            print(f"{L} {p:11s} {g:6s}: R {a.mean():.3f} (n={len(a)}), RE low {b2.mean():.3f} (n={len(b2)}), KS D={ks.statistic:.3f} p={ks.pvalue:.2e}, holds {ok}")
        n_ok[p] = k
    print(f"{L}: groups that hold, reliability {n_ok['reliability']} of 3, out/in {n_ok['out_in']} of 3")
L1 reliability start : R 0.529 (n=54), RE low 0.257 (n=28), KS D=0.705 p=2.77e-09, holds True
L1 reliability middle: R 0.580 (n=124), RE low 0.400 (n=82), KS D=0.445 p=2.52e-09, holds True
L1 reliability end   : R 0.526 (n=67), RE low 0.317 (n=41), KS D=0.528 p=4.74e-07, holds True
L1 out_in      start : R 0.073 (n=54), RE low 0.093 (n=28), KS D=0.234 p=2.18e-01, holds False
L1 out_in      middle: R 0.065 (n=124), RE low 0.085 (n=82), KS D=0.254 p=2.70e-03, holds True
L1 out_in      end   : R 0.070 (n=67), RE low 0.087 (n=41), KS D=0.305 p=1.31e-02, holds False
L1: groups that hold, reliability 3 of 3, out/in 1 of 3
L2 reliability start : R 0.529 (n=54), RE low 0.268 (n=25), KS D=0.661 p=1.17e-07, holds True
L2 reliability middle: R 0.580 (n=124), RE low 0.405 (n=83), KS D=0.425 p=1.29e-08, holds True
L2 reliability end   : R 0.526 (n=67), RE low 0.319 (n=41), KS D=0.528 p=4.74e-07, holds True
L2 out_in      start : R 0.073 (n=54), RE low 0.100 (n=25), KS D=0.294 p=8.18e-02, holds False
L2 out_in      middle: R 0.065 (n=124), RE low 0.087 (n=83), KS D=0.248 p=3.28e-03, holds True
L2 out_in      end   : R 0.070 (n=67), RE low 0.087 (n=41), KS D=0.281 p=2.80e-02, holds False
L2: groups that hold, reliability 3 of 3, out/in 1 of 3
L3 reliability start : R 0.529 (n=54), RE low 0.275 (n=33), KS D=0.658 p=5.58e-09, holds True
L3 reliability middle: R 0.580 (n=124), RE low 0.385 (n=76), KS D=0.471 p=5.47e-10, holds True
L3 reliability end   : R 0.526 (n=67), RE low 0.318 (n=37), KS D=0.551 p=3.40e-07, holds True
L3 out_in      start : R 0.073 (n=54), RE low 0.107 (n=33), KS D=0.264 p=9.24e-02, holds False
L3 out_in      middle: R 0.065 (n=124), RE low 0.089 (n=76), KS D=0.253 p=3.77e-03, holds True
L3 out_in      end   : R 0.070 (n=67), RE low 0.082 (n=37), KS D=0.229 p=1.37e-01, holds False
L3: groups that hold, reliability 3 of 3, out/in 1 of 3
L1c reliability start : R 0.513 (n=172), RE low 0.285 (n=67), KS D=0.577 p=1.76e-15, holds True
L1c reliability middle: R 0.616 (n=327), RE low 0.382 (n=190), KS D=0.469 p=2.05e-24, holds True
L1c reliability end   : R 0.554 (n=248), RE low 0.333 (n=75), KS D=0.458 p=1.69e-11, holds True
L1c out_in      start : R 0.087 (n=172), RE low 0.108 (n=67), KS D=0.221 p=1.45e-02, holds False
L1c out_in      middle: R 0.081 (n=327), RE low 0.107 (n=190), KS D=0.228 p=5.53e-06, holds True
L1c out_in      end   : R 0.059 (n=248), RE low 0.095 (n=75), KS D=0.307 p=2.59e-05, holds True
L1c: groups that hold, reliability 3 of 3, out/in 2 of 3

Result: partially reproduced. Under L1, reliability is lower in RE low at the start, middle and end (KS p 4.7e-07 to 2.8e-09), and the out/in ratio is higher in the middle (p = 2.7e-03) but not at the start (p = 0.22) or the end (p = 0.013): 3 groups for one measure, which the criterion counts as partial; L2 and L3 give the same, and L1c gives 3 and 2 groups.

supp11-all-mice-parameters¶

With all mice and both engaged and disengaged RE low laps, place field medians show the same degradation and return.

In [26]:
EXP11 = {"reliability": (0.48, 0.26, 0.44), "out_in": (0.10, 0.19, 0.13), "width": (50.21, 54.96, 45.71), "intensity": (0.34, 0.26, 0.28)}
supp11 = {}
for L in K.LICK_READINGS:
    pf = {"R": pd.concat([fields_df(s, "R") for s in WT]), "RE low": pd.concat([fields_df(s, f"RElow_all_{L}") for s in WT]),
          "RR": pd.concat([fields_df(s, "RR") for s in K.WT_FULL])}
    for reading in ["(a) median of per-mouse means", "(b) median of pooled fields"]:
        n_in = 0
        dirs = []
        line = []
        for p, ex in EXP11.items():
            v = []
            for c in ["R", "RE low", "RR"]:
                if reading.startswith("(a)"):
                    v.append(pf[c].groupby("mouse")[p].mean().median())
                else:
                    v.append(pf[c][p].median())
            n_in += sum(abs(x - e) <= 0.1 * e for x, e in zip(v, ex))
            dirs.append((v[1] < v[0]) if p in ("reliability", "intensity") else (v[1] > v[0]))
            line.append(f"{p} " + "/".join(f"{x:.3f}" for x in v))
        supp11[(L, reading)] = (n_in, all(dirs))
        print(f"{L} {reading}: " + "; ".join(line) + f" | within 10%: {n_in} of 12; directions hold: {all(dirs)} {dirs}")
    print(f"    fields: R {len(pf['R'])} (10 mice), RE low {len(pf['RE low'])}, RR {len(pf['RR'])} (5 mice)")
L1 (a) median of per-mouse means: reliability 0.565/0.391/0.506; out_in 0.075/0.098/0.082; width 76.950/80.816/77.101; intensity 0.441/0.320/0.368 | within 10%: 0 of 12; directions hold: True [np.True_, np.True_, np.True_, np.True_]
L1 (b) median of pooled fields: reliability 0.537/0.327/0.494; out_in 0.060/0.096/0.059; width 75.000/80.000/75.000; intensity 0.310/0.274/0.305 | within 10%: 3 of 12; directions hold: True [np.True_, np.True_, np.True_, np.True_]
    fields: R 1674 (10 mice), RE low 945, RR 821 (5 mice)
L2 (a) median of per-mouse means: reliability 0.565/0.387/0.506; out_in 0.075/0.098/0.082; width 76.950/79.830/77.101; intensity 0.441/0.317/0.368 | within 10%: 0 of 12; directions hold: True [np.True_, np.True_, np.True_, np.True_]
L2 (b) median of pooled fields: reliability 0.537/0.330/0.494; out_in 0.060/0.098/0.059; width 75.000/80.000/75.000; intensity 0.310/0.272/0.305 | within 10%: 3 of 12; directions hold: True [np.True_, np.True_, np.True_, np.True_]
    fields: R 1674 (10 mice), RE low 884, RR 821 (5 mice)
L3 (a) median of per-mouse means: reliability 0.565/0.405/0.506; out_in 0.075/0.096/0.082; width 76.950/81.246/77.101; intensity 0.441/0.320/0.368 | within 10%: 0 of 12; directions hold: True [np.True_, np.True_, np.True_, np.True_]
L3 (b) median of pooled fields: reliability 0.537/0.328/0.494; out_in 0.060/0.098/0.059; width 75.000/80.000/75.000; intensity 0.310/0.275/0.305 | within 10%: 3 of 12; directions hold: True [np.True_, np.True_, np.True_, np.True_]
    fields: R 1674 (10 mice), RE low 841, RR 821 (5 mice)

Result: not reproduced. With all 10 mice and all RE low laps, every direction holds (reliability and intensity lower and out/in ratio and width higher in RE low), but only 3 of the 12 medians are within 10% under reading (b) and none under reading (a); the criterion asks for 10 (7 for partial). Widths are 75 to 81 cm against 46 to 55 cm. This claim does not use the engagement split.

fig2e-place-field-fates¶

Of 605 R place fields (3 mice), 169 (27.9%) stay stable through R, RE low and RR; in RE low 222 (36.7%) stay stable, 249 (41.2%) disappear and 134 (22.1%) remap, and 296 of the 663 RE low fields (44.6%) are new. In RR 311 R fields (51.4%) are stable and 89 (14.7%) remapped, 126 of the 249 lost fields (50.6%) reappear, 64 of the 296 new RE low fields (21.6%) stay and 204 (68.9%) disappear, and 140 of 674 RR fields (20.8%) are new.

In [27]:
def is_stable(f_early, f_late, rule):
    if rule == "F1":
        return f_late["start"] < f_early["end"] and f_early["start"] < f_late["end"]
    if rule == "F2":
        return f_early["start"] * K.BIN <= f_late["com"] < f_early["end"] * K.BIN
    return group_of(np.array([f_early["com"]]))[0] == group_of(np.array([f_late["com"]]))[0]

def match(f, later_cell_fields):
    if later_cell_fields is None or len(later_cell_fields) == 0:
        return None
    i = np.argmin(np.abs(later_cell_fields["com"].values - f["com"]))
    return later_cell_fields.iloc[i]

def fates(L, rule):
    out = {k: 0 for k in ["R", "RE low", "RR", "stable through", "stable RE low", "lost RE low", "remap RE low", "new RE low",
                          "stable RR", "remap RR", "lost RR", "lost reappear RR", "new low stable RR", "new low lost RR", "new RR (vs RE low)", "new RR (vs R and RE low)",
                          "lost end", "lost end reappear"]}
    newlow_bins = np.zeros(4, int)
    newrr_bins = np.zeros(4, int)
    for s in FIG23[L]:
        R, Lo, RR = fields_df(s, "R"), fields_df(s, low_name(L)), fields_df(s, "RR")
        gR, gL, gRR = R.groupby("cell"), Lo.groupby("cell"), RR.groupby("cell")
        cR, cL, cRR = set(R["cell"]), set(Lo["cell"]), set(RR["cell"])
        out["R"] += len(R); out["RE low"] += len(Lo); out["RR"] += len(RR)
        for _, f in R.iterrows():
            ml = match(f, gL.get_group(f["cell"]) if f["cell"] in cL else None)
            mr = match(f, gRR.get_group(f["cell"]) if f["cell"] in cRR else None)
            st_low = ml is not None and is_stable(f, ml, rule)
            st_rr = mr is not None and is_stable(f, mr, rule)
            if ml is None:
                out["lost RE low"] += 1
                if f["com"] >= 150:
                    out["lost end"] += 1
                if st_rr:
                    out["lost reappear RR"] += 1
                    if f["com"] >= 150:
                        out["lost end reappear"] += 1
            elif st_low:
                out["stable RE low"] += 1
            else:
                out["remap RE low"] += 1
            if mr is None:
                out["lost RR"] += 1
            elif st_rr:
                out["stable RR"] += 1
            else:
                out["remap RR"] += 1
            if st_low and st_rr:
                out["stable through"] += 1
        for _, f in Lo.iterrows():
            if f["cell"] not in cR:
                out["new RE low"] += 1
                newlow_bins[min(int(f["com"] // 50), 3)] += 1
                mr = match(f, gRR.get_group(f["cell"]) if f["cell"] in cRR else None)
                if mr is None:
                    out["new low lost RR"] += 1
                elif is_stable(f, mr, rule):
                    out["new low stable RR"] += 1
        for _, f in RR.iterrows():
            if f["cell"] not in cL:
                out["new RR (vs RE low)"] += 1
                if f["cell"] not in cR:
                    out["new RR (vs R and RE low)"] += 1
                    newrr_bins[min(int(f["com"] // 50), 3)] += 1
    return out, newlow_bins, newrr_bins

FATES = {}
for L in READS:
    for rule in ["F1", "F2", "F3"]:
        o, nlb, nrb = fates(L, rule)
        FATES[(L, rule)] = (o, nlb, nrb)
        pct = lambda a, b: 100 * a / b if b else np.nan
        vals = {"stable through": pct(o["stable through"], o["R"]), "stable RE low": pct(o["stable RE low"], o["R"]),
                "lost RE low": pct(o["lost RE low"], o["R"]), "remap RE low": pct(o["remap RE low"], o["R"]),
                "new RE low": pct(o["new RE low"], o["RE low"]), "stable RR": pct(o["stable RR"], o["R"]),
                "remap RR": pct(o["remap RR"], o["R"]), "lost reappear": pct(o["lost reappear RR"], o["lost RE low"]),
                "new low stable RR": pct(o["new low stable RR"], o["new RE low"]), "new low lost RR": pct(o["new low lost RR"], o["new RE low"]),
                "new RR (vs R and RE low)": pct(o["new RR (vs R and RE low)"], o["RR"]), "new RR (vs RE low)": pct(o["new RR (vs RE low)"], o["RR"])}
        FATES[(L, rule)] = (o, nlb, nrb, vals)
        print(f"{L} {rule}: fields R {o['R']}, RE low {o['RE low']}, RR {o['RR']} | " + "; ".join(f"{k} {v:.1f}%" for k, v in vals.items()))
EXP2E = {"stable through": (27.9, 2.8), "stable RE low": (36.7, 3.7), "lost RE low": (41.2, 4.1), "remap RE low": (22.1, 2.2), "new RE low": (44.6, 4.5),
         "stable RR": (51.4, 5.1), "remap RR": (14.7, 1.5), "lost reappear": (50.6, 5.1), "new low stable RR": (21.6, 2.2), "new low lost RR": (68.9, 6.9),
         "new RR (vs R and RE low)": (20.8, 2.1)}
for k, (o, nlb, nrb, vals) in FATES.items():
    w = [n for n, (v, tol) in EXP2E.items() if abs(vals[n] - v) <= tol]
    print(k, f"within tolerance: {len(w)} of {len(EXP2E)}:", w)
L1 F1: fields R 245, RE low 151, RR 254 | stable through 11.0%; stable RE low 19.6%; lost RE low 75.9%; remap RE low 4.5%; new RE low 60.3%; stable RR 40.4%; remap RR 9.0%; lost reappear 34.9%; new low stable RR 41.8%; new low lost RR 53.8%; new RR (vs R and RE low) 35.8%; new RR (vs RE low) 69.3%
L1 F2: fields R 245, RE low 151, RR 254 | stable through 8.2%; stable RE low 15.9%; lost RE low 75.9%; remap RE low 8.2%; new RE low 60.3%; stable RR 33.9%; remap RR 15.5%; lost reappear 30.1%; new low stable RR 41.8%; new low lost RR 53.8%; new RR (vs R and RE low) 35.8%; new RR (vs RE low) 69.3%
L1 F3: fields R 245, RE low 151, RR 254 | stable through 11.4%; stable RE low 19.6%; lost RE low 75.9%; remap RE low 4.5%; new RE low 60.3%; stable RR 39.6%; remap RR 9.8%; lost reappear 34.4%; new low stable RR 37.4%; new low lost RR 53.8%; new RR (vs R and RE low) 35.8%; new RR (vs RE low) 69.3%
L2 F1: fields R 245, RE low 149, RR 254 | stable through 10.2%; stable RE low 19.6%; lost RE low 75.9%; remap RE low 4.5%; new RE low 59.1%; stable RR 40.4%; remap RR 9.0%; lost reappear 36.0%; new low stable RR 44.3%; new low lost RR 51.1%; new RR (vs R and RE low) 35.4%; new RR (vs RE low) 69.7%
L2 F2: fields R 245, RE low 149, RR 254 | stable through 7.3%; stable RE low 15.9%; lost RE low 75.9%; remap RE low 8.2%; new RE low 59.1%; stable RR 33.9%; remap RR 15.5%; lost reappear 31.7%; new low stable RR 43.2%; new low lost RR 51.1%; new RR (vs R and RE low) 35.4%; new RR (vs RE low) 69.7%
L2 F3: fields R 245, RE low 149, RR 254 | stable through 11.0%; stable RE low 20.0%; lost RE low 75.9%; remap RE low 4.1%; new RE low 59.1%; stable RR 39.6%; remap RR 9.8%; lost reappear 35.5%; new low stable RR 38.6%; new low lost RR 51.1%; new RR (vs R and RE low) 35.4%; new RR (vs RE low) 69.7%
L3 F1: fields R 245, RE low 146, RR 254 | stable through 9.8%; stable RE low 19.6%; lost RE low 75.5%; remap RE low 4.9%; new RE low 57.5%; stable RR 40.4%; remap RR 9.0%; lost reappear 37.8%; new low stable RR 40.5%; new low lost RR 54.8%; new RR (vs R and RE low) 37.8%; new RR (vs RE low) 72.4%
L3 F2: fields R 245, RE low 146, RR 254 | stable through 7.3%; stable RE low 16.3%; lost RE low 75.5%; remap RE low 8.2%; new RE low 57.5%; stable RR 33.9%; remap RR 15.5%; lost reappear 32.4%; new low stable RR 36.9%; new low lost RR 54.8%; new RR (vs R and RE low) 37.8%; new RR (vs RE low) 72.4%
L3 F3: fields R 245, RE low 146, RR 254 | stable through 10.2%; stable RE low 20.0%; lost RE low 75.5%; remap RE low 4.5%; new RE low 57.5%; stable RR 39.6%; remap RR 9.8%; lost reappear 37.3%; new low stable RR 35.7%; new low lost RR 54.8%; new RR (vs R and RE low) 37.8%; new RR (vs RE low) 72.4%
L1c F1: fields R 747, RE low 332, RR 736 | stable through 12.4%; stable RE low 19.0%; lost RE low 77.5%; remap RE low 3.5%; new RE low 49.4%; stable RR 45.8%; remap RR 6.8%; lost reappear 41.6%; new low stable RR 36.6%; new low lost RR 56.7%; new RR (vs R and RE low) 36.7%; new RR (vs RE low) 75.0%
L1c F2: fields R 747, RE low 332, RR 736 | stable through 10.6%; stable RE low 16.9%; lost RE low 77.5%; remap RE low 5.6%; new RE low 49.4%; stable RR 42.0%; remap RR 10.6%; lost reappear 38.3%; new low stable RR 35.4%; new low lost RR 56.7%; new RR (vs R and RE low) 36.7%; new RR (vs RE low) 75.0%
L1c F3: fields R 747, RE low 332, RR 736 | stable through 10.8%; stable RE low 18.2%; lost RE low 77.5%; remap RE low 4.3%; new RE low 49.4%; stable RR 43.1%; remap RR 9.5%; lost reappear 39.4%; new low stable RR 33.5%; new low lost RR 56.7%; new RR (vs R and RE low) 36.7%; new RR (vs RE low) 75.0%
('L1', 'F1') within tolerance: 0 of 11: []
('L1', 'F2') within tolerance: 1 of 11: ['remap RR']
('L1', 'F3') within tolerance: 0 of 11: []
('L2', 'F1') within tolerance: 0 of 11: []
('L2', 'F2') within tolerance: 1 of 11: ['remap RR']
('L2', 'F3') within tolerance: 0 of 11: []
('L3', 'F1') within tolerance: 0 of 11: []
('L3', 'F2') within tolerance: 1 of 11: ['remap RR']
('L3', 'F3') within tolerance: 0 of 11: []
('L1c', 'F1') within tolerance: 0 of 11: []
('L1c', 'F2') within tolerance: 0 of 11: []
('L1c', 'F3') within tolerance: 0 of 11: []

Result: not reproduced. In the 2 qualifying mice, 75.9% of the 245 R fields disappear in RE low under L1 (paper 41.2%) and 60.3% of the 151 RE low fields are new (paper 44.6%); these do not depend on the stable/remapped definitions. Under F1 to F3, 0 or 1 of the 11 percentages is within tolerance under any lick stop reading, and 0 under L1c (4 mice, 77.5% lost).

reward-zone-field-turnover¶

Of the R place fields that disappear in RE low, 41.8% (104 of 249) are at the track end (150 to 200 cm); new RE low fields are spread evenly (26, 28, 21 and 25% per 50 cm). In RR only 61 of the 104 lost end fields return (58.6%), and new RR fields cluster at the end (31, 24, 18 and 67 of 140, 48% at 150 to 200 cm).

In [28]:
for k, (o, nlb, nrb, vals) in FATES.items():
    print(f"{k}: lost {o['lost RE low']}, of them at 150 to 200 cm {o['lost end']} ({100 * o['lost end'] / max(1, o['lost RE low']):.1f}%); "
          f"new RE low per 50 cm {nlb.tolist()} ({np.round(100 * nlb / max(1, nlb.sum())).astype(int).tolist()}%); "
          f"lost end fields reappearing in RR {o['lost end reappear']}; new RR per 50 cm {nrb.tolist()} ({np.round(100 * nrb / max(1, nrb.sum())).astype(int).tolist()}%)")
('L1', 'F1'): lost 186, of them at 150 to 200 cm 50 (26.9%); new RE low per 50 cm [21, 16, 30, 24] ([23, 18, 33, 26]%); lost end fields reappearing in RR 18; new RR per 50 cm [29, 18, 17, 27] ([32, 20, 19, 30]%)
('L1', 'F2'): lost 186, of them at 150 to 200 cm 50 (26.9%); new RE low per 50 cm [21, 16, 30, 24] ([23, 18, 33, 26]%); lost end fields reappearing in RR 15; new RR per 50 cm [29, 18, 17, 27] ([32, 20, 19, 30]%)
('L1', 'F3'): lost 186, of them at 150 to 200 cm 50 (26.9%); new RE low per 50 cm [21, 16, 30, 24] ([23, 18, 33, 26]%); lost end fields reappearing in RR 18; new RR per 50 cm [29, 18, 17, 27] ([32, 20, 19, 30]%)
('L2', 'F1'): lost 186, of them at 150 to 200 cm 51 (27.4%); new RE low per 50 cm [17, 18, 28, 25] ([19, 20, 32, 28]%); lost end fields reappearing in RR 20; new RR per 50 cm [31, 16, 17, 26] ([34, 18, 19, 29]%)
('L2', 'F2'): lost 186, of them at 150 to 200 cm 51 (27.4%); new RE low per 50 cm [17, 18, 28, 25] ([19, 20, 32, 28]%); lost end fields reappearing in RR 17; new RR per 50 cm [31, 16, 17, 26] ([34, 18, 19, 29]%)
('L2', 'F3'): lost 186, of them at 150 to 200 cm 51 (27.4%); new RE low per 50 cm [17, 18, 28, 25] ([19, 20, 32, 28]%); lost end fields reappearing in RR 20; new RR per 50 cm [31, 16, 17, 26] ([34, 18, 19, 29]%)
('L3', 'F1'): lost 185, of them at 150 to 200 cm 51 (27.6%); new RE low per 50 cm [20, 18, 23, 23] ([24, 21, 27, 27]%); lost end fields reappearing in RR 21; new RR per 50 cm [29, 17, 22, 28] ([30, 18, 23, 29]%)
('L3', 'F2'): lost 185, of them at 150 to 200 cm 51 (27.6%); new RE low per 50 cm [20, 18, 23, 23] ([24, 21, 27, 27]%); lost end fields reappearing in RR 18; new RR per 50 cm [29, 17, 22, 28] ([30, 18, 23, 29]%)
('L3', 'F3'): lost 185, of them at 150 to 200 cm 51 (27.6%); new RE low per 50 cm [20, 18, 23, 23] ([24, 21, 27, 27]%); lost end fields reappearing in RR 21; new RR per 50 cm [29, 17, 22, 28] ([30, 18, 23, 29]%)
('L1c', 'F1'): lost 579, of them at 150 to 200 cm 200 (34.5%); new RE low per 50 cm [40, 41, 46, 37] ([24, 25, 28, 23]%); lost end fields reappearing in RR 87; new RR per 50 cm [83, 36, 45, 106] ([31, 13, 17, 39]%)
('L1c', 'F2'): lost 579, of them at 150 to 200 cm 200 (34.5%); new RE low per 50 cm [40, 41, 46, 37] ([24, 25, 28, 23]%); lost end fields reappearing in RR 78; new RR per 50 cm [83, 36, 45, 106] ([31, 13, 17, 39]%)
('L1c', 'F3'): lost 579, of them at 150 to 200 cm 200 (34.5%); new RE low per 50 cm [40, 41, 46, 37] ([24, 25, 28, 23]%); lost end fields reappearing in RR 84; new RR per 50 cm [83, 36, 45, 106] ([31, 13, 17, 39]%)

Result: not reproduced. Of the 186 R fields lost in RE low under L1, 50 (26.9%) are at 150 to 200 cm, against 104 of 249 (41.8%); new RE low fields are 23, 18, 33 and 26% per 50 cm (paper 26, 28, 21 and 25%) and number 91, not 296; 15 to 21 lost end fields reappear in RR (paper 61), and new RR fields are 29, 18, 17 and 27 per 50 cm (paper 31, 24, 18 and 67). Most counts are outside tolerance under every reading: the lost end fields, the four bins of new RE low fields, the lost end fields that reappear in RR, and new RR fields at 50 to 100 cm (16 to 18 against 24 +/- 2.4) and at 150 to 200 cm (26 to 28 against 67). Two are inside: new RR fields at 0 to 50 cm (29, 31 and 29 under L1 to L3, against 31 +/- 3.1) and at 100 to 150 cm under L1 and L2 (17, against 18 +/- 1.8; 22 under L3); with 4 mice (L1c) 200 of 579 lost fields (34.5%) are at the end.

fig3d-reward-overrepresentation¶

Place fields over-represent the reward site at the track end in R; the over-representation disappears in RE low and reappears in RR. Gaussian fits to field COMs are 114 +/- 55 cm in R, 102 +/- 54 in UR and 112 +/- 58 in RR.

In [29]:
def end_minus_middle(df):
    com = df["com"].dropna().values
    if len(com) == 0:
        return np.nan
    h = np.histogram(com, bins=[0, 50, 100, 150, 200])[0] / len(com) * 100
    return h[3] - h[1:3].mean()

for L in READS:
    em = {c: [end_minus_middle(PF[L][c][PF[L][c]["mouse"] == s]) for s in FIG23[L]] for c in PF[L]}
    gauss = {c: stats.norm.fit(PF[L][c]["com"].dropna()) for c in PF[L]}
    print(f"--- {L}, mice {FIG23[L]}")
    print("   end minus middle (% of fields) per mouse:", {c: np.round(v, 1).tolist() for c, v in em.items()})
    print("   Gaussian fit mean +/- SD (cm):", {c: (round(m, 1), round(sd, 1)) for c, (m, sd) in gauss.items()})
    for c, v in em.items():
        t = stats.ttest_1samp(v, 0)
        print(f"   {c}: mean {np.mean(v):.1f}, t-test against 0 p={t.pvalue:.3f}")
    print("   R > 0 in all mice:", all(np.array(em["R"]) > 0), "| RR > 0 in all mice:", all(np.array(em["RR"]) > 0),
          "| RE low below R in all mice:", all(np.array(em["RE low"]) < np.array(em["R"])), "| RE low mean <= 0:", np.mean(em["RE low"]) <= 0)
--- L1, mice ['NR6', 'NR23']
   end minus middle (% of fields) per mouse: {'R': [6.8, -4.2], 'RE low': [-14.6, 5.5], 'RR': [-5.7, 9.5]}
   Gaussian fit mean +/- SD (cm): {'R': (np.float64(106.4), np.float64(58.6)), 'RE low': (np.float64(108.4), np.float64(54.7)), 'RR': (np.float64(103.8), np.float64(57.9))}
   R: mean 1.3, t-test against 0 p=0.854
   RE low: mean -4.6, t-test against 0 p=0.727
   RR: mean 1.9, t-test against 0 p=0.843
   R > 0 in all mice: False | RR > 0 in all mice: False | RE low below R in all mice: False | RE low mean <= 0: True
--- L2, mice ['NR6', 'NR23']
   end minus middle (% of fields) per mouse: {'R': [6.8, -4.2], 'RE low': [-19.2, 6.4], 'RR': [-5.7, 9.5]}
   Gaussian fit mean +/- SD (cm): {'R': (np.float64(106.4), np.float64(58.6)), 'RE low': (np.float64(109.3), np.float64(54.3)), 'RR': (np.float64(103.8), np.float64(57.9))}
   R: mean 1.3, t-test against 0 p=0.854
   RE low: mean -6.4, t-test against 0 p=0.703
   RR: mean 1.9, t-test against 0 p=0.843
   R > 0 in all mice: False | RR > 0 in all mice: False | RE low below R in all mice: False | RE low mean <= 0: True
--- L3, mice ['NR6', 'NR23']
   end minus middle (% of fields) per mouse: {'R': [6.8, -4.2], 'RE low': [-9.1, 2.9], 'RR': [-5.7, 9.5]}
   Gaussian fit mean +/- SD (cm): {'R': (np.float64(106.4), np.float64(58.6)), 'RE low': (np.float64(103.7), np.float64(57.7)), 'RR': (np.float64(103.8), np.float64(57.9))}
   R: mean 1.3, t-test against 0 p=0.854
   RE low: mean -3.1, t-test against 0 p=0.699
   RR: mean 1.9, t-test against 0 p=0.843
   R > 0 in all mice: False | RR > 0 in all mice: False | RE low below R in all mice: False | RE low mean <= 0: True
--- L1c, mice ['NR6', 'NR21', 'NR23', 'CFC4']
   end minus middle (% of fields) per mouse: {'R': [6.8, 13.1, -4.2, 17.7], 'RE low': [-16.0, -7.6, 1.5, -7.4], 'RR': [-5.7, 14.1, 9.5, 17.0]}
   Gaussian fit mean +/- SD (cm): {'R': (np.float64(111.7), np.float64(60.0)), 'RE low': (np.float64(102.1), np.float64(54.4)), 'RR': (np.float64(108.8), np.float64(60.1))}
   R: mean 8.3, t-test against 0 p=0.177
   RE low: mean -7.4, t-test against 0 p=0.130
   RR: mean 8.7, t-test against 0 p=0.182
   R > 0 in all mice: False | RR > 0 in all mice: False | RE low below R in all mice: False | RE low mean <= 0: True

Result: not reproduced. In the 2 qualifying mice the end minus middle excess is 6.8 and -4.2 points in R, so R does not over-represent the reward site in every mouse; in RE low it is -14.6 and 5.5 (mean -4.6, not above 0) and it is not below R in NR23; in RR it is -5.7 and 9.5. The Gaussian fits are within tolerance (R 106.4 +/- 58.6, RE low 108.4 +/- 54.7, RR 103.8 +/- 57.9 cm under L1). With 4 mice (L1c), R is above 0 in 3 of 4 mice (6.8, 13.1, -4.2, 17.7), RE low is below R in 3 of 4 mice with a mean of -7.4, and RR is above 0 in 3 of 4.

fig2a-pv-cluster-transition¶

Lap by lap population vector correlations of R place cells show a transition around the lick stop (RE high to RE low) and again after reward returns; the probability of laps belonging to the R cluster dips from RE high to RE low, not from R to RE high, and rises to some extent in RR (fully only in mouse 1).

Per lap, the population vector is each R place cell's mean dF/F in each of the 40 bins. Laps: R, RE high, RE low engaged, RR. k-means with 3 clusters, run 1000 times from random starts (seeded); the R cluster is the one holding most R laps in each run.

In [30]:
from sklearn.cluster import KMeans
rng_pv = np.random.default_rng(2)
fig2a = {}


def pv_clusters(s, L, n_r=None, tag=""):
    """R-cluster probability per lap set; n_r keeps only the first n_r R laps (variant)."""
    cells = np.array(sorted(fields_df(s, "R")["cell"].unique()))
    b = RES[s]["beh"]
    ls = b["lick_stop"][lsr(L)]
    nR = RES[s]["maps"]["Rewarded"].shape[1] if n_r is None else n_r
    parts = [("R", "Rewarded", list(range(nR))),
             ("RE high", "Unrewarded", list(range(ls))),
             ("RE low", "Unrewarded", eng_laps(s, L)),
             ("RR", "ReRewarded", list(range(RES[s]["maps"]["ReRewarded"].shape[1])))]
    X, lab = [], []
    for name, cond, laps in parts:
        m = RES[s]["maps"][cond][cells][:, laps]  # cells x laps x 40
        X.append(m.transpose(1, 0, 2).reshape(len(laps), -1))
        lab += [name] * len(laps)
    X = np.vstack(X)
    lab = np.array(lab)
    pvc = np.corrcoef(X)
    prob = np.zeros(len(X))
    n_runs = 1000
    for i in range(n_runs):
        km = KMeans(3, n_init=1, random_state=int(rng_pv.integers(1 << 31))).fit(X)
        rc = np.bincount(km.labels_[lab == "R"], minlength=3).argmax()
        prob += km.labels_ == rc
    prob /= n_runs
    mp = {n: prob[lab == n].mean() for n in ["R", "RE high", "RE low", "RR"]}
    ok = abs(mp["RE high"] - mp["R"]) <= 0.2 and mp["RE low"] <= mp["RE high"] - 0.3
    mean_pvc = {n: pvc[np.ix_(lab == n, lab == "R")].mean() for n in ["R", "RE high", "RE low", "RR"]}
    print(f"{L}{tag} {s}: {len(cells)} R place cells, laps {dict(zip(*np.unique(lab, return_counts=True)))}; mean R-cluster probability "
          + ", ".join(f"{n} {v:.2f}" for n, v in mp.items()) + f"; holds {ok}")
    print("    mean PV correlation with R laps: " + ", ".join(f"{n} {v:.3f}" for n, v in mean_pvc.items()))
    print("    lap-wise probability:", " ".join(f"{x:.2f}" for x in prob))
    return ok

for L in READS:
    holds = 0
    for s in FIG23[L]:
        ok = pv_clusters(s, L)
        holds += ok
    fig2a[L] = holds
    print(f"{L}: holds in {holds} of {len(FIG23[L])} mice")

# Variant: the paper's code keeps only the first 11100 R frames of NR6 (task_framestokeep), here the
# R laps that end within them; the R place cells stay those found on all R laps
n_nr6 = code_kept_laps("NR6", 0, RES["NR6"]["beh"]["conds"]["Rewarded"])
print(f"NR6 R laps kept by the code's frame cut: {n_nr6} of {RES['NR6']['maps']['Rewarded'].shape[1]}")
for L in READS:
    if "NR6" in FIG23[L]:
        pv_clusters("NR6", L, n_r=n_nr6, tag=" (NR6 R cut)")
L1 NR6: 134 R place cells, laps {np.str_('R'): np.int64(43), np.str_('RE high'): np.int64(12), np.str_('RE low'): np.int64(18), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.87, RE high 0.10, RE low 0.05, RR 0.05; holds False
    mean PV correlation with R laps: R 0.487, RE high 0.278, RE low 0.180, RR 0.253
    lap-wise probability: 0.81 0.83 0.86 0.86 0.85 0.86 0.86 0.86 0.86 0.86 0.62 0.85 0.88 0.92 0.86 0.88 0.90 0.91 0.93 0.98 0.94 0.96 0.95 0.97 0.92 0.93 0.92 0.91 0.91 0.91 0.91 0.90 0.90 0.90 0.91 0.91 0.91 0.90 0.90 0.90 0.08 0.92 0.91 0.29 0.10 0.08 0.09 0.09 0.09 0.09 0.09 0.08 0.09 0.07 0.06 0.05 0.05 0.05 0.05 0.06 0.04 0.06 0.05 0.06 0.05 0.05 0.05 0.06 0.06 0.06 0.05 0.05 0.05 0.05 0.05 0.05 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.05 0.06 0.06 0.05 0.05 0.05 0.06
L1 NR23: 106 R place cells, laps {np.str_('R'): np.int64(40), np.str_('RE high'): np.int64(2), np.str_('RE low'): np.int64(26), np.str_('RR'): np.int64(37)}; mean R-cluster probability R 0.82, RE high 0.49, RE low 0.23, RR 0.27; holds False
    mean PV correlation with R laps: R 0.431, RE high 0.193, RE low 0.139, RR 0.187
    lap-wise probability: 0.13 0.14 0.17 0.91 0.39 0.91 0.21 0.43 0.43 0.81 0.94 0.89 0.90 0.94 0.89 0.90 0.94 0.98 0.95 0.94 0.90 0.91 0.92 0.98 1.00 0.93 0.99 0.98 0.93 0.93 0.98 0.90 0.91 0.97 0.93 0.94 0.98 0.92 0.94 0.99 0.85 0.12 0.11 0.13 0.10 0.14 0.14 0.80 0.17 0.13 0.20 0.88 0.12 0.14 0.14 0.21 0.14 0.38 0.14 0.26 0.15 0.30 0.13 0.39 0.42 0.12 0.13 0.11 0.21 0.34 0.12 0.27 0.40 0.42 0.39 0.37 0.36 0.46 0.43 0.37 0.38 0.39 0.39 0.39 0.38 0.33 0.37 0.37 0.38 0.33 0.12 0.12 0.12 0.12 0.17 0.21 0.12 0.14 0.12 0.11 0.10 0.13 0.13 0.21 0.13
L1: holds in 0 of 2 mice
L2 NR6: 134 R place cells, laps {np.str_('R'): np.int64(43), np.str_('RE high'): np.int64(13), np.str_('RE low'): np.int64(18), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.87, RE high 0.10, RE low 0.06, RR 0.06; holds False
    mean PV correlation with R laps: R 0.487, RE high 0.262, RE low 0.180, RR 0.253
    lap-wise probability: 0.81 0.83 0.85 0.85 0.84 0.85 0.85 0.85 0.85 0.85 0.59 0.84 0.87 0.92 0.85 0.87 0.90 0.91 0.93 0.98 0.94 0.94 0.94 0.96 0.92 0.93 0.92 0.90 0.90 0.90 0.90 0.89 0.90 0.89 0.90 0.90 0.90 0.88 0.90 0.90 0.09 0.90 0.90 0.29 0.09 0.08 0.09 0.09 0.09 0.09 0.09 0.08 0.08 0.07 0.07 0.09 0.05 0.05 0.06 0.05 0.06 0.04 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.05 0.06 0.06
L2 NR23: 106 R place cells, laps {np.str_('R'): np.int64(40), np.str_('RE high'): np.int64(3), np.str_('RE low'): np.int64(26), np.str_('RR'): np.int64(37)}; mean R-cluster probability R 0.84, RE high 0.36, RE low 0.26, RR 0.31; holds False
    mean PV correlation with R laps: R 0.431, RE high 0.136, RE low 0.139, RR 0.187
    lap-wise probability: 0.15 0.16 0.25 0.89 0.44 0.90 0.30 0.47 0.47 0.87 0.93 0.93 0.94 0.93 0.93 0.94 0.93 0.98 0.93 0.93 0.95 0.96 0.92 0.99 1.00 0.92 0.99 0.98 0.92 0.96 0.99 0.94 0.95 0.98 0.92 0.93 0.98 0.96 0.96 0.99 0.81 0.14 0.14 0.14 0.15 0.10 0.16 0.16 0.75 0.23 0.15 0.28 0.86 0.14 0.15 0.16 0.23 0.17 0.42 0.16 0.32 0.15 0.36 0.15 0.44 0.50 0.15 0.15 0.13 0.22 0.37 0.15 0.34 0.46 0.48 0.44 0.43 0.42 0.53 0.49 0.42 0.43 0.44 0.44 0.44 0.44 0.38 0.43 0.42 0.43 0.38 0.14 0.14 0.15 0.14 0.22 0.24 0.13 0.16 0.14 0.12 0.12 0.15 0.15 0.24 0.16
L2: holds in 0 of 2 mice
L3 NR6: 134 R place cells, laps {np.str_('R'): np.int64(43), np.str_('RE high'): np.int64(14), np.str_('RE low'): np.int64(17), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.87, RE high 0.10, RE low 0.05, RR 0.06; holds False
    mean PV correlation with R laps: R 0.487, RE high 0.248, RE low 0.187, RR 0.253
    lap-wise probability: 0.82 0.83 0.85 0.85 0.84 0.85 0.85 0.85 0.85 0.85 0.57 0.84 0.87 0.90 0.85 0.86 0.89 0.91 0.93 0.98 0.95 0.96 0.96 0.96 0.93 0.94 0.93 0.91 0.90 0.91 0.91 0.90 0.91 0.90 0.90 0.91 0.90 0.90 0.90 0.90 0.08 0.89 0.90 0.29 0.10 0.08 0.09 0.09 0.09 0.09 0.09 0.08 0.09 0.07 0.06 0.09 0.05 0.06 0.05 0.06 0.05 0.04 0.05 0.05 0.06 0.05 0.06 0.05 0.06 0.06 0.06 0.05 0.06 0.05 0.05 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.05 0.06 0.06
L3 NR23: 106 R place cells, laps {np.str_('R'): np.int64(40), np.str_('RE high'): np.int64(4), np.str_('RE low'): np.int64(25), np.str_('RR'): np.int64(37)}; mean R-cluster probability R 0.85, RE high 0.31, RE low 0.28, RR 0.33; holds False
    mean PV correlation with R laps: R 0.431, RE high 0.105, RE low 0.144, RR 0.187
    lap-wise probability: 0.16 0.18 0.27 0.91 0.49 0.91 0.32 0.54 0.53 0.89 0.93 0.94 0.95 0.93 0.95 0.95 0.93 0.98 0.93 0.93 0.96 0.96 0.92 0.98 0.99 0.92 0.99 0.97 0.92 0.97 0.99 0.95 0.96 0.96 0.92 0.92 0.97 0.97 0.97 0.99 0.81 0.15 0.14 0.14 0.15 0.12 0.18 0.18 0.75 0.26 0.17 0.31 0.87 0.16 0.17 0.17 0.26 0.18 0.47 0.18 0.34 0.16 0.40 0.17 0.48 0.52 0.15 0.17 0.14 0.25 0.43 0.16 0.36 0.49 0.50 0.48 0.46 0.46 0.54 0.51 0.46 0.47 0.48 0.48 0.48 0.48 0.42 0.46 0.46 0.47 0.42 0.14 0.15 0.15 0.16 0.23 0.25 0.14 0.17 0.14 0.13 0.13 0.16 0.16 0.25 0.17
L3: holds in 0 of 2 mice
L1c NR6: 134 R place cells, laps {np.str_('R'): np.int64(43), np.str_('RE high'): np.int64(12), np.str_('RE low'): np.int64(18), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.87, RE high 0.10, RE low 0.05, RR 0.06; holds False
    mean PV correlation with R laps: R 0.487, RE high 0.278, RE low 0.176, RR 0.253
    lap-wise probability: 0.83 0.83 0.86 0.86 0.85 0.86 0.86 0.86 0.86 0.86 0.61 0.86 0.88 0.93 0.86 0.87 0.90 0.92 0.94 0.97 0.95 0.96 0.96 0.97 0.94 0.94 0.93 0.91 0.91 0.91 0.91 0.91 0.91 0.90 0.91 0.91 0.91 0.90 0.91 0.90 0.08 0.91 0.91 0.27 0.09 0.08 0.09 0.09 0.09 0.08 0.09 0.08 0.09 0.07 0.06 0.06 0.05 0.06 0.05 0.05 0.04 0.06 0.06 0.06 0.05 0.05 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.06 0.05 0.06 0.06 0.06 0.05 0.06 0.06
L1c NR21: 202 R place cells, laps {np.str_('R'): np.int64(40), np.str_('RE high'): np.int64(5), np.str_('RE low'): np.int64(15), np.str_('RR'): np.int64(22)}; mean R-cluster probability R 0.84, RE high 0.82, RE low 0.15, RR 0.02; holds True
    mean PV correlation with R laps: R 0.643, RE high 0.516, RE low 0.313, RR 0.370
    lap-wise probability: 0.67 0.76 0.56 0.55 0.55 0.57 0.64 0.58 0.90 0.65 0.96 0.95 0.96 0.89 0.97 0.92 0.97 0.91 0.89 0.92 0.95 0.96 0.90 0.88 0.89 0.95 0.91 0.87 0.88 0.88 0.85 0.88 0.94 0.86 0.86 0.83 0.83 0.87 0.95 0.90 0.90 0.59 0.90 0.84 0.86 0.43 0.11 0.43 0.32 0.01 0.10 0.01 0.01 0.01 0.01 0.01 0.01 0.08 0.65 0.01 0.03 0.01 0.01 0.01 0.01 0.03 0.02 0.01 0.02 0.03 0.01 0.02 0.01 0.01 0.01 0.01 0.01 0.03 0.01 0.01 0.01 0.01
L1c NR23: 106 R place cells, laps {np.str_('R'): np.int64(40), np.str_('RE high'): np.int64(2), np.str_('RE low'): np.int64(26), np.str_('RR'): np.int64(37)}; mean R-cluster probability R 0.83, RE high 0.49, RE low 0.27, RR 0.30; holds False
    mean PV correlation with R laps: R 0.431, RE high 0.193, RE low 0.139, RR 0.187
    lap-wise probability: 0.16 0.18 0.25 0.88 0.43 0.89 0.30 0.48 0.48 0.82 0.92 0.91 0.91 0.93 0.91 0.92 0.92 0.97 0.93 0.93 0.93 0.93 0.91 0.97 0.99 0.92 0.98 0.97 0.92 0.94 0.98 0.92 0.92 0.96 0.91 0.92 0.97 0.93 0.94 0.98 0.83 0.15 0.14 0.16 0.13 0.18 0.18 0.78 0.23 0.17 0.28 0.87 0.15 0.18 0.22 0.18 0.41 0.18 0.33 0.20 0.33 0.17 0.42 0.47 0.21 0.15 0.16 0.12 0.27 0.38 0.16 0.32 0.42 0.44 0.41 0.40 0.39 0.48 0.45 0.39 0.40 0.41 0.41 0.41 0.41 0.37 0.40 0.39 0.40 0.36 0.14 0.15 0.16 0.16 0.21 0.22 0.15 0.17 0.15 0.14 0.15 0.17 0.17 0.22 0.17
L1c CFC4: 299 R place cells, laps {np.str_('R'): np.int64(39), np.str_('RE high'): np.int64(8), np.str_('RE low'): np.int64(23), np.str_('RR'): np.int64(37)}; mean R-cluster probability R 0.89, RE high 0.72, RE low 0.14, RR 0.19; holds True
    mean PV correlation with R laps: R 0.532, RE high 0.417, RE low 0.184, RR 0.394
    lap-wise probability: 0.06 0.38 0.89 0.91 0.91 0.91 0.91 0.87 0.91 0.94 0.93 0.97 0.37 0.97 0.98 1.00 0.99 1.00 1.00 1.00 1.00 1.00 0.99 0.99 0.99 0.99 0.99 0.99 0.98 0.97 0.98 0.98 0.22 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.15 0.36 0.50 0.96 0.98 0.95 0.87 0.49 0.11 0.20 0.14 0.31 0.12 0.12 0.17 0.09 0.09 0.10 0.09 0.09 0.10 0.13 0.11 0.15 0.12 0.10 0.10 0.10 0.10 0.08 0.09 0.12 0.15 0.21 0.20 0.22 0.19 0.18 0.21 0.21 0.21 0.21 0.21 0.20 0.20 0.21 0.20 0.20 0.21 0.21 0.20 0.12 0.20 0.21 0.21 0.21 0.20 0.21 0.21 0.20 0.20 0.21 0.20 0.19 0.19 0.19 0.19
L1c: holds in 2 of 4 mice
NR6 R laps kept by the code's frame cut: 24 of 43
L1 (NR6 R cut) NR6: 134 R place cells, laps {np.str_('R'): np.int64(24), np.str_('RE high'): np.int64(12), np.str_('RE low'): np.int64(18), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.96, RE high 0.08, RE low 0.09, RR 0.11; holds False
    mean PV correlation with R laps: R 0.549, RE high 0.253, RE low 0.169, RR 0.252
    lap-wise probability: 0.93 0.95 0.97 0.98 0.97 0.97 0.98 0.98 0.97 0.98 0.59 0.97 0.98 0.97 0.98 0.99 0.97 0.99 0.97 0.97 0.98 0.98 0.97 0.98 0.15 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.06 0.06 0.08 0.09 0.10 0.08 0.11 0.05 0.06 0.09 0.11 0.12 0.09 0.09 0.09 0.10 0.12 0.09 0.10 0.10 0.09 0.09 0.11 0.11 0.12 0.12 0.12 0.12 0.11 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.11 0.11 0.11 0.12 0.12 0.09 0.12 0.12
L2 (NR6 R cut) NR6: 134 R place cells, laps {np.str_('R'): np.int64(24), np.str_('RE high'): np.int64(13), np.str_('RE low'): np.int64(18), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.96, RE high 0.09, RE low 0.12, RR 0.14; holds False
    mean PV correlation with R laps: R 0.549, RE high 0.238, RE low 0.169, RR 0.252
    lap-wise probability: 0.94 0.95 0.97 0.98 0.97 0.98 0.98 0.98 0.98 0.99 0.59 0.97 0.99 0.97 0.98 0.99 0.98 0.98 0.98 0.98 0.98 0.98 0.97 0.98 0.17 0.09 0.08 0.09 0.09 0.09 0.08 0.09 0.08 0.08 0.08 0.09 0.12 0.11 0.13 0.11 0.13 0.07 0.07 0.12 0.13 0.14 0.11 0.12 0.12 0.12 0.14 0.12 0.12 0.12 0.12 0.11 0.14 0.13 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.13 0.14 0.14 0.14 0.14 0.12 0.14 0.14
L3 (NR6 R cut) NR6: 134 R place cells, laps {np.str_('R'): np.int64(24), np.str_('RE high'): np.int64(14), np.str_('RE low'): np.int64(17), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.96, RE high 0.10, RE low 0.11, RR 0.13; holds False
    mean PV correlation with R laps: R 0.549, RE high 0.226, RE low 0.175, RR 0.252
    lap-wise probability: 0.93 0.96 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.57 0.98 0.98 0.96 0.98 0.98 0.97 0.98 0.96 0.96 0.97 0.98 0.96 0.98 0.18 0.09 0.08 0.09 0.09 0.09 0.08 0.09 0.08 0.08 0.08 0.10 0.11 0.10 0.13 0.09 0.13 0.06 0.07 0.11 0.12 0.13 0.10 0.11 0.11 0.11 0.14 0.11 0.11 0.11 0.10 0.11 0.13 0.13 0.14 0.14 0.14 0.14 0.14 0.13 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.13 0.13 0.14 0.14 0.14 0.11 0.14 0.14
L1c (NR6 R cut) NR6: 134 R place cells, laps {np.str_('R'): np.int64(24), np.str_('RE high'): np.int64(12), np.str_('RE low'): np.int64(18), np.str_('RR'): np.int64(25)}; mean R-cluster probability R 0.96, RE high 0.09, RE low 0.10, RR 0.12; holds False
    mean PV correlation with R laps: R 0.549, RE high 0.253, RE low 0.164, RR 0.252
    lap-wise probability: 0.94 0.95 0.98 0.98 0.97 0.98 0.98 0.99 0.98 0.99 0.60 0.98 0.98 0.98 0.98 0.98 0.97 0.98 0.96 0.97 0.97 0.97 0.97 0.98 0.15 0.08 0.08 0.08 0.08 0.09 0.08 0.08 0.08 0.08 0.07 0.07 0.09 0.10 0.09 0.11 0.05 0.06 0.10 0.11 0.12 0.09 0.10 0.10 0.13 0.10 0.09 0.10 0.09 0.09 0.09 0.11 0.12 0.13 0.13 0.13 0.13 0.13 0.12 0.13 0.13 0.13 0.13 0.12 0.13 0.13 0.13 0.12 0.12 0.12 0.13 0.12 0.10 0.13 0.13

Result: not reproduced. In the 2 qualifying mice the rule holds in neither under any lick stop reading: in NR6 the R-cluster probability falls already at the first UR lap (R 0.87, RE high 0.10, RE low 0.05), and in NR23, with 2 to 4 RE high laps, RE high is already low (0.49 against R 0.82 under L1, RE low 0.23). With 4 mice (L1c, a variant) it holds in 2 of 4 (NR21: R 0.84, RE high 0.82, RE low 0.15; CFC4: 0.89, 0.72, 0.14), the paper's pattern. RR probabilities are low (0.02 to 0.33). Keeping only the first 24 of NR6's 43 R laps, as the paper's code does (task_framestokeep, 11100 frames; R place cells unchanged), NR6 still fails under every reading (R 0.96, RE high 0.08 to 0.10, RE low 0.09 to 0.12); NR6's Figure 2 and 3 field values with fields found on those 24 laps were not computed.

supp13-novel-environment¶

In a novel, never reinforced environment, decoding and place field reliability and out/in ratio are poor in early laps and improve over the session as mice develop reward expectation, becoming comparable to R and RR; RE low reliability and out/in ratio match the early novel laps.

Five of the six mice with a novel session are in the dandiset. Early = first 12 novel laps, late = last 12. Decoders are trained and tested within each condition (sliding 20% test block), each condition down-sampled to the fewest laps; for early and late R2 the novel session is decoded on all its laps. Place field parameters in a block are read two ways: fields defined in the block, or fields defined over the whole novel session and measured on the block's laps.

In [31]:
def block_params(s, name_all, laps):
    """Parameters of the session's novel fields measured on a block of laps."""
    df = fields_df(s, name_all)
    M = RES[s]["maps"]["Novel"][:, laps]
    out = []
    for _, f in df.iterrows():
        lm = M[int(f["cell"])]
        p = K.field_parameters(lm, lm > 0, int(f["start"]), int(f["end"]))
        out.append(p)
    return pd.DataFrame(out)

def prelicks_per_lap(c, zone):
    """Licks in the reward zone before the reward is delivered, per lap (pre-licking)."""
    out = []
    for a, e in K.complete_laps(c):
        rw = c["rewards"][(c["rewards"] >= a) & (c["rewards"] < e)]
        stop = rw[0] if len(rw) else e
        lk = c["licks"][(c["licks"] >= a) & (c["licks"] < stop)]
        out.append(int((c["pos"][lk] >= zone).sum()))
    return np.array(out)

rows = []
for s in K.WT_FULL:
    b = RES[s]["beh"]
    cN = b["conds"]["Novel"]
    nN = len(K.complete_laps(cN))
    early, late = list(range(min(12, nN))), list(range(max(0, nN - 12), nN))
    zl = prelicks_per_lap(cN, b["zone"])
    cv = RES[s]["dec_cv_novel_all"]
    r = {"mouse": s, "novel laps": nN,
         "pre-licking early": np.mean(zl[early]), "pre-licking late": np.mean(zl[late]),
         "R2 early": np.nanmean(cv[early]), "R2 late": np.nanmean(cv[late]),
         "R2 R (cv)": np.nanmean(RES[s]["dec_cv"]["Rewarded"]), "R2 RR (cv)": np.nanmean(RES[s]["dec_cv"]["ReRewarded"]),
         "R2 N (cv)": np.nanmean(RES[s]["dec_cv"]["Novel"])}
    for p in ["reliability", "out_in"]:
        r[f"{p} early (block fields)"] = fields_df(s, "N_early")[p].mean()
        r[f"{p} late (block fields)"] = fields_df(s, "N_late")[p].mean()
        r[f"{p} early (session fields)"] = block_params(s, "N", early)[p].mean()
        r[f"{p} late (session fields)"] = block_params(s, "N", late)[p].mean()
        r[f"{p} R"] = fields_df(s, "R")[p].mean()
        r[f"{p} RR"] = fields_df(s, "RR")[p].mean()
        r[f"{p} RE low (L1, all laps)"] = fields_df(s, "RElow_all_L1")[p].mean()
    rows.append(r)
nov = pd.DataFrame(rows).set_index("mouse")
print(nov.T.round(3).to_string())
tests = {}
for label, a, b2, better in [("pre-licking", "pre-licking early", "pre-licking late", 1), ("decoder R2", "R2 early", "R2 late", 1),
                             ("reliability (block fields)", "reliability early (block fields)", "reliability late (block fields)", 1),
                             ("reliability (session fields)", "reliability early (session fields)", "reliability late (session fields)", 1),
                             ("out/in (block fields)", "out_in early (block fields)", "out_in late (block fields)", -1),
                             ("out/in (session fields)", "out_in early (session fields)", "out_in late (session fields)", -1)]:
    d = nov[[a, b2]].dropna()
    t = stats.ttest_rel(d[b2], d[a])
    ok = np.sign(d[b2].mean() - d[a].mean()) == better and t.pvalue < 0.05
    tests[label] = ok
    print(f"{label:30s}: early {d[a].mean():.3f}, late {d[b2].mean():.3f}, paired t={t.statistic:.2f} p={t.pvalue:.4f} (n={len(d)}); improves at p < 0.05: {ok}")
# scale of the session-field out/in ratio on a block of 12 laps: fields with little activity in the block give large ratios
for s in K.WT_FULL:
    nN = len(K.complete_laps(RES[s]["beh"]["conds"]["Novel"]))
    for blk, laps in [("early", list(range(min(12, nN)))), ("late", list(range(max(0, nN - 12), nN)))]:
        bp = block_params(s, "N", laps)["out_in"]
        print(f"{s} session fields, {blk} block: out/in median {bp.median():.3f}, mean {bp.mean():.3f}, undefined (no activity in field) {bp.isna().sum()} of {len(bp)}, above 1: {(bp > 1).sum()}")
mouse                                  NR6    NR14    NR21    NR23    CFC4
novel laps                          33.000  20.000  20.000  50.000  41.000
pre-licking early                    2.917   2.500   0.083   0.917   2.500
pre-licking late                     3.417  10.333   0.167   1.333   1.417
R2 early                             0.766   0.812   0.800   0.844   0.883
R2 late                              0.943   0.896   0.856   0.943   0.957
R2 R (cv)                            0.969   0.907   0.989   0.981   0.979
R2 RR (cv)                           0.974   0.870   0.971   0.911   0.971
R2 N (cv)                            0.835   0.839   0.817   0.941   0.920
reliability early (block fields)     0.253   0.414   0.348   0.283   0.301
reliability late (block fields)      0.371   0.433   0.455   0.387   0.312
reliability early (session fields)   0.215   0.291   0.228   0.186   0.232
reliability late (session fields)    0.473   0.444   0.446   0.470   0.336
reliability R                        0.612   0.560   0.674   0.477   0.516
reliability RR                       0.597   0.476   0.584   0.393   0.506
reliability RE low (L1, all laps)    0.438   0.401   0.489   0.301   0.199
out_in early (block fields)          0.065   0.112   0.142   0.063   0.072
out_in late (block fields)           0.094   0.093   0.117   0.046   0.050
out_in early (session fields)        1.015   0.162   0.211   0.800   0.560
out_in late (session fields)         0.168   0.099   0.139   0.200   0.412
out_in R                             0.070   0.093   0.091   0.066   0.069
out_in RR                            0.068   0.088   0.105   0.082   0.066
out_in RE low (L1, all laps)         0.092   0.086   0.118   0.109   0.146
pre-licking                   : early 1.783, late 3.333, paired t=0.97 p=0.3865 (n=5); improves at p < 0.05: False
decoder R2                    : early 0.821, late 0.919, paired t=4.71 p=0.0093 (n=5); improves at p < 0.05: True
reliability (block fields)    : early 0.320, late 0.392, paired t=3.07 p=0.0375 (n=5); improves at p < 0.05: True
reliability (session fields)  : early 0.230, late 0.434, paired t=6.14 p=0.0036 (n=5); improves at p < 0.05: True
out/in (block fields)         : early 0.091, late 0.080, paired t=-1.08 p=0.3418 (n=5); improves at p < 0.05: False
out/in (session fields)       : early 0.550, late 0.203, paired t=-2.17 p=0.0961 (n=5); improves at p < 0.05: False
NR6 session fields, early block: out/in median 0.145, mean 1.015, undefined (no activity in field) 11 of 71, above 1: 7
NR6 session fields, late block: out/in median 0.083, mean 0.168, undefined (no activity in field) 0 of 71, above 1: 2
NR14 session fields, early block: out/in median 0.121, mean 0.162, undefined (no activity in field) 0 of 72, above 1: 1
NR14 session fields, late block: out/in median 0.070, mean 0.099, undefined (no activity in field) 0 of 72, above 1: 0
NR21 session fields, early block: out/in median 0.158, mean 0.211, undefined (no activity in field) 1 of 79, above 1: 0
NR21 session fields, late block: out/in median 0.080, mean 0.139, undefined (no activity in field) 0 of 79, above 1: 1
NR23 session fields, early block: out/in median 0.132, mean 0.800, undefined (no activity in field) 22 of 123, above 1: 9
NR23 session fields, late block: out/in median 0.040, mean 0.200, undefined (no activity in field) 13 of 123, above 1: 3
CFC4 session fields, early block: out/in median 0.122, mean 0.560, undefined (no activity in field) 18 of 247, above 1: 20
CFC4 session fields, late block: out/in median 0.061, mean 0.412, undefined (no activity in field) 5 of 247, above 1: 8

Result: partially reproduced. In the 5 novel mice, late laps decode better than early laps (R2 0.821 to 0.919, p = 0.0093) and have higher reliability (block fields 0.320 to 0.392, p = 0.038; session fields 0.230 to 0.434, p = 0.0036). Pre-licking in the reward zone before the reward (p = 0.39) and the out/in ratio (p = 0.34 and 0.096) do not change significantly: 2 of the 4 tests hold. The session-field out/in means (0.550 early) are pulled up by fields with almost no activity in a 12-lap block (up to 20 fields with a ratio above 1 per mouse); their medians are 0.121 to 0.158 early and 0.040 to 0.083 late.

DREADD mice¶

Four DAT-Cre hM4D(Gi) mice (sub-009, 011, 012, 014); the paper has six (five for saline). Day 1 (R, UR, RR) and days 2 to 4 (before and after saline, CNO and DCZ) are stored back to back in one file per mouse; 012 has no RR.

In [32]:
PAIRS = {"saline": ("BeforeSaline", "AfterSaline"), "DCZ": ("BeforeDCZ", "AfterDCZ"), "CNO": ("BeforeCNO", "AfterCNO")}
for s in DR:
    b = RES[s]["beh"]
    print(s, {n: len(K.complete_laps(c)) for n, c in b["conds"].items()}, "lick stop", b["lick_stop"], "engaged RE low (L1)", len(eng_laps(s, "L1")))
009 {'Rewarded': 28, 'Unrewarded': 19, 'ReRewarded': 21, 'BeforeSaline': 29, 'AfterSaline': 44, 'BeforeCNO': 24, 'AfterCNO': 25, 'BeforeDCZ': 23, 'AfterDCZ': 25} lick stop {'L1': 8, 'L2': 9, 'L3': 10} engaged RE low (L1) 2
011 {'Rewarded': 54, 'Unrewarded': 24, 'ReRewarded': 38, 'BeforeSaline': 44, 'AfterSaline': 65, 'BeforeCNO': 49, 'AfterCNO': 51, 'BeforeDCZ': 46, 'AfterDCZ': 51} lick stop {'L1': 17, 'L2': 18, 'L3': 19} engaged RE low (L1) 7
012 {'Rewarded': 40, 'Unrewarded': 29, 'BeforeSaline': 37, 'AfterSaline': 68, 'BeforeCNO': 34, 'AfterCNO': 31, 'BeforeDCZ': 32, 'AfterDCZ': 45} lick stop {'L1': 10, 'L2': 11, 'L3': 12} engaged RE low (L1) 9
014 {'Rewarded': 54, 'Unrewarded': 46, 'ReRewarded': 41, 'BeforeSaline': 47, 'AfterSaline': 91, 'BeforeCNO': 29, 'AfterCNO': 38, 'BeforeDCZ': 51, 'AfterDCZ': 27} lick stop {'L1': 2, 'L2': 3, 'L3': 4} engaged RE low (L1) 32

supp16-dreadd-running-speed¶

DCZ and CNO slow lap running speed in R (from about 42 to about 26), while saline does not, and approach behaviour and anticipatory licking remain.

In [33]:
EXP16 = {"BeforeSaline": (42.74, 1.62), "AfterSaline": (42.75, 1.42), "BeforeDCZ": (41.99, 1.74), "AfterDCZ": (25.96, 1.66), "BeforeCNO": (43.18, 1.92), "AfterCNO": (25.71, 1.68)}

def run_speed(c):
    return np.array([np.mean(c["speed"][a:e][c["speed"][a:e] >= K.RUN_SPEED]) for a, e in K.complete_laps(c)])

def prelick_laps(c, zone):
    """Fraction of laps with a lick in the reward zone before the reward."""
    out = []
    for a, e in K.complete_laps(c):
        rw = c["rewards"][(c["rewards"] >= a) & (c["rewards"] < e)]
        stop = rw[0] if len(rw) else e
        lk = c["licks"][(c["licks"] >= a) & (c["licks"] < stop)]
        out.append(bool((c["pos"][lk] >= zone).any()))
    return np.mean(out)

for measure, fn in [("lap speed 200 cm / lap time", lambda c: K.lap_speeds(c)), ("mean running speed", run_speed)]:
    print(f"--- {measure} (cm/s)")
    for reading in ["laps pooled", "per-mouse means"]:
        vals = {}
        for n in EXP16:
            per = [fn(RES[s]["beh"]["conds"][n]) for s in DR]
            vals[n] = mean_ci(np.concatenate(per)) if reading == "laps pooled" else mean_ci([np.nanmean(p) for p in per])
        within = {n: abs(vals[n][0] - v) <= tol for n, (v, tol) in EXP16.items()}
        print(f"  {reading}: " + "; ".join(f"{n} {m:.2f} [{m - h:.2f} {m + h:.2f}]" for n, (m, h) in vals.items()) + f" | within {sum(within.values())} of 6")
    for drug, (bn, an) in PAIRS.items():
        before = np.concatenate([fn(RES[s]["beh"]["conds"][bn]) for s in DR])
        after = np.concatenate([fn(RES[s]["beh"]["conds"][an]) for s in DR])
        t = stats.ttest_ind(after[np.isfinite(after)], before[np.isfinite(before)])
        print(f"  {drug}: after minus before {np.nanmean(after) - np.nanmean(before):.2f}, unpaired t over laps t={t.statistic:.2f} p={t.pvalue:.2e} ({np.isfinite(before).sum()} and {np.isfinite(after).sum()} laps)")
for s in DR:
    print(s, "lap speed per condition:", {n: round(np.nanmean(K.lap_speeds(RES[s]["beh"]["conds"][n])), 1) for n in EXP16},
          "| laps with a pre-reward lick in the zone:", {n: round(prelick_laps(RES[s]["beh"]["conds"][n], RES[s]["beh"]["zone"]), 2) for n in EXP16},
          "| approach ratio (main reading):", {n: round(np.nanmean(RES[s]["beh"][K.ENGAGE_MAIN][n]), 2) for n in EXP16})
--- lap speed 200 cm / lap time (cm/s)
  laps pooled: BeforeSaline 25.94 [24.83 27.04]; AfterSaline 26.15 [25.13 27.17]; BeforeDCZ 27.24 [25.86 28.62]; AfterDCZ 13.54 [12.42 14.65]; BeforeCNO 27.60 [25.79 29.42]; AfterCNO 14.56 [13.68 15.44] | within 0 of 6
  per-mouse means: BeforeSaline 25.01 [15.96 34.07]; AfterSaline 25.39 [13.90 36.89]; BeforeDCZ 24.96 [11.35 38.57]; AfterDCZ 13.30 [5.65 20.95]; BeforeCNO 26.72 [8.89 44.55]; AfterCNO 14.15 [8.94 19.36] | within 0 of 6
  saline: after minus before 0.22, unpaired t over laps t=0.28 p=7.82e-01 (156 and 241 laps)
  DCZ: after minus before -13.70, unpaired t over laps t=-15.02 p=7.87e-38 (150 and 133 laps)
  CNO: after minus before -13.05, unpaired t over laps t=-12.81 p=1.96e-29 (130 and 131 laps)
--- mean running speed (cm/s)
  laps pooled: BeforeSaline 31.90 [30.76 33.03]; AfterSaline 32.43 [31.45 33.42]; BeforeDCZ 32.56 [31.30 33.81]; AfterDCZ 19.41 [18.28 20.54]; BeforeCNO 33.35 [31.63 35.07]; AfterCNO 20.36 [19.33 21.38] | within 0 of 6
  per-mouse means: BeforeSaline 30.92 [20.73 41.11]; AfterSaline 30.70 [17.44 43.96]; BeforeDCZ 30.50 [17.55 43.45]; AfterDCZ 18.36 [8.67 28.05]; BeforeCNO 32.37 [13.60 51.13]; AfterCNO 19.09 [10.20 27.98] | within 0 of 6
  saline: after minus before 0.54, unpaired t over laps t=0.68 p=4.98e-01 (157 and 268 laps)
  DCZ: after minus before -13.15, unpaired t over laps t=-15.36 p=1.32e-39 (152 and 148 laps)
  CNO: after minus before -13.00, unpaired t over laps t=-13.02 p=1.33e-30 (136 and 145 laps)
009 lap speed per condition: {'BeforeSaline': np.float64(17.9), 'AfterSaline': np.float64(16.3), 'BeforeDCZ': np.float64(15.0), 'AfterDCZ': np.float64(10.6), 'BeforeCNO': np.float64(14.5), 'AfterCNO': np.float64(11.2)} | laps with a pre-reward lick in the zone: {'BeforeSaline': np.float64(0.83), 'AfterSaline': np.float64(0.98), 'BeforeDCZ': np.float64(1.0), 'AfterDCZ': np.float64(1.0), 'BeforeCNO': np.float64(1.0), 'AfterCNO': np.float64(0.96)} | approach ratio (main reading): {'BeforeSaline': np.float64(2.43), 'AfterSaline': np.float64(2.31), 'BeforeDCZ': np.float64(2.57), 'AfterDCZ': np.float64(1.94), 'BeforeCNO': np.float64(2.18), 'AfterCNO': np.float64(2.67)}
011 lap speed per condition: {'BeforeSaline': np.float64(28.1), 'AfterSaline': np.float64(25.3), 'BeforeDCZ': np.float64(31.1), 'AfterDCZ': np.float64(20.5), 'BeforeCNO': np.float64(31.1), 'AfterCNO': np.float64(18.6)} | laps with a pre-reward lick in the zone: {'BeforeSaline': np.float64(1.0), 'AfterSaline': np.float64(1.0), 'BeforeDCZ': np.float64(1.0), 'AfterDCZ': np.float64(1.0), 'BeforeCNO': np.float64(1.0), 'AfterCNO': np.float64(0.98)} | approach ratio (main reading): {'BeforeSaline': np.float64(1.62), 'AfterSaline': np.float64(2.18), 'BeforeDCZ': np.float64(2.05), 'AfterDCZ': np.float64(3.02), 'BeforeCNO': np.float64(1.79), 'AfterCNO': np.float64(2.42)}
012 lap speed per condition: {'BeforeSaline': np.float64(23.3), 'AfterSaline': np.float64(25.9), 'BeforeDCZ': np.float64(20.7), 'AfterDCZ': np.float64(11.2), 'BeforeCNO': np.float64(21.2), 'AfterCNO': np.float64(12.3)} | laps with a pre-reward lick in the zone: {'BeforeSaline': np.float64(0.95), 'AfterSaline': np.float64(0.79), 'BeforeDCZ': np.float64(1.0), 'AfterDCZ': np.float64(0.98), 'BeforeCNO': np.float64(1.0), 'AfterCNO': np.float64(0.81)} | approach ratio (main reading): {'BeforeSaline': np.float64(2.04), 'AfterSaline': np.float64(2.75), 'BeforeDCZ': np.float64(3.54), 'AfterDCZ': np.float64(2.77), 'BeforeCNO': np.float64(2.61), 'AfterCNO': np.float64(2.13)}
014 lap speed per condition: {'BeforeSaline': np.float64(30.8), 'AfterSaline': np.float64(34.0), 'BeforeDCZ': np.float64(33.0), 'AfterDCZ': np.float64(10.9), 'BeforeCNO': np.float64(40.0), 'AfterCNO': np.float64(14.5)} | laps with a pre-reward lick in the zone: {'BeforeSaline': np.float64(0.96), 'AfterSaline': np.float64(0.98), 'BeforeDCZ': np.float64(1.0), 'AfterDCZ': np.float64(0.96), 'BeforeCNO': np.float64(0.55), 'AfterCNO': np.float64(0.89)} | approach ratio (main reading): {'BeforeSaline': np.float64(4.53), 'AfterSaline': np.float64(2.9), 'BeforeDCZ': np.float64(3.76), 'AfterDCZ': np.float64(3.59), 'BeforeCNO': np.float64(2.51), 'AfterCNO': np.float64(3.46)}

Result: not reproduced. DCZ and CNO lower lap speed (after minus before -13.70 and -13.05 cm/s, unpaired t p = 7.9e-38 and 2.0e-29) and saline does not (+0.22, p = 0.78), and nearly all laps after DCZ and CNO still have a pre-reward lick in the zone. But none of the six means is within tolerance: 25.9 to 27.6 cm/s before and 13.5 to 14.6 cm/s after, against about 42 and 26, with the 4 of 6 mice in the dandiset. The criterion needs the six means. The timing check shows that the clock agrees with the reward pause, and lap speed is the 200 cm track over the lap time, so it does not depend on the volts-to-cm scale; the slower speeds are in the data or in a lap speed measure the paper does not define.

fig4f-dreadd-spatial-correlation¶

Spatial correlation of place fields before and after DCZ (and CNO) falls at all track locations, as in RE low, while it does not fall after saline.

In [34]:
def pair_corr(s, first, second, second_laps=None):
    df = fields_df(s, first)
    cells = np.array(sorted(df["cell"].unique()))
    c1 = first if first in RES[s]["maps"] else RES[s]["fields"][first]["cond"]
    laps1 = None if first in RES[s]["maps"] else RES[s]["fields"][first]["laps"]
    m1 = mean_map(s, c1, cells, laps1)
    m2 = mean_map(s, second, cells, second_laps)
    return pd.DataFrame({"mouse": s, "cell": cells, "com": cell_com(df).reindex(cells).values, "r": rowcorr(m1, m2)})

def dreadd_corrs(L, lowset):
    out = {}
    rows = []
    for s in DR:
        b = RES[s]["beh"]
        low = eng_laps(s, L) if lowset == "engaged" else list(range(b["lick_stop"][L], len(b["engaged_ur"][K.ENGAGE_MAIN])))
        rows.append(pair_corr(s, "Rewarded", "Unrewarded", low))
    out["R/RE low"] = pd.concat(rows, ignore_index=True)
    for drug, (bn, an) in PAIRS.items():
        out[drug] = pd.concat([pair_corr(s, bn, an) for s in DR], ignore_index=True)
    return out

EXP4F = {"R/RE low": (928, 93), "saline": (1139, 114), "DCZ": (1629, 163), "CNO": (1498, 150)}
fig4f = {}
for L in K.LICK_READINGS:
    for lowset in ["engaged", "all"]:
        co = dreadd_corrs(L, lowset)
        fig4f[(L, lowset)] = co
        print(f"--- {L}, RE low {lowset} laps")
        print("   place cells:", {k: (len(v), "within" if abs(len(v) - EXP4F[k][0]) <= EXP4F[k][1] else "outside") for k, v in co.items()},
              "| mean r:", {k: round(v["r"].mean(), 3) for k, v in co.items()})
        for drug in ["DCZ", "CNO"]:
            ks = stats.ks_2samp(co[drug]["r"].dropna(), co["saline"]["r"].dropna())
            print(f"   {drug} vs saline overall: mean {co[drug]['r'].mean():.3f} vs {co['saline']['r'].mean():.3f}, KS D={ks.statistic:.3f} p={ks.pvalue:.2e}")
        for g in ["start", "middle", "end"]:
            sel = {k: v[group_of(v["com"].values) == g]["r"].dropna() for k, v in co.items()}
            line = []
            for k in ["saline", "DCZ", "CNO"]:
                ks = stats.ks_2samp(sel["R/RE low"], sel[k])
                line.append(f"R/RE low vs {k} KS p={ks.pvalue:.1e}")
            ks2 = {k: stats.ks_2samp(sel[k], sel["saline"]).pvalue for k in ["DCZ", "CNO"]}
            print(f"   {g:6s}: mean r " + ", ".join(f"{k} {v.mean():.3f} (n={len(v)})" for k, v in sel.items()) + " | " + "; ".join(line)
                  + f"; DCZ vs saline p={ks2['DCZ']:.1e}, CNO vs saline p={ks2['CNO']:.1e}")
print("per mouse mean r (L1, engaged):")
print(pd.DataFrame({k: v.groupby("mouse")["r"].mean() for k, v in fig4f[("L1", "engaged")].items()}).round(3).to_string())
--- L1, RE low engaged laps
   place cells: {'R/RE low': (610, 'outside'), 'saline': (676, 'outside'), 'DCZ': (842, 'outside'), 'CNO': (641, 'outside')} | mean r: {'R/RE low': np.float64(0.351), 'saline': np.float64(0.793), 'DCZ': np.float64(0.657), 'CNO': np.float64(0.653)}
   DCZ vs saline overall: mean 0.657 vs 0.793, KS D=0.220 p=1.87e-16
   CNO vs saline overall: mean 0.653 vs 0.793, KS D=0.202 p=3.32e-12
   start : mean r R/RE low 0.321 (n=110), saline 0.860 (n=137), DCZ 0.643 (n=157), CNO 0.679 (n=126) | R/RE low vs saline KS p=4.3e-21; R/RE low vs DCZ KS p=9.1e-07; R/RE low vs CNO KS p=2.3e-08; DCZ vs saline p=8.4e-07, CNO vs saline p=4.1e-04
   middle: mean r R/RE low 0.403 (n=263), saline 0.798 (n=352), DCZ 0.664 (n=403), CNO 0.628 (n=301) | R/RE low vs saline KS p=2.7e-28; R/RE low vs DCZ KS p=1.7e-10; R/RE low vs CNO KS p=3.7e-07; DCZ vs saline p=1.5e-10, CNO vs saline p=1.0e-08
   end   : mean r R/RE low 0.299 (n=202), saline 0.735 (n=187), DCZ 0.654 (n=281), CNO 0.673 (n=214) | R/RE low vs saline KS p=1.5e-25; R/RE low vs DCZ KS p=1.2e-17; R/RE low vs CNO KS p=5.9e-17; DCZ vs saline p=2.4e-02, CNO vs saline p=8.0e-02
--- L1, RE low all laps
   place cells: {'R/RE low': (610, 'outside'), 'saline': (676, 'outside'), 'DCZ': (842, 'outside'), 'CNO': (641, 'outside')} | mean r: {'R/RE low': np.float64(0.36), 'saline': np.float64(0.793), 'DCZ': np.float64(0.657), 'CNO': np.float64(0.653)}
   DCZ vs saline overall: mean 0.657 vs 0.793, KS D=0.220 p=1.87e-16
   CNO vs saline overall: mean 0.653 vs 0.793, KS D=0.202 p=3.32e-12
   start : mean r R/RE low 0.366 (n=114), saline 0.860 (n=137), DCZ 0.643 (n=157), CNO 0.679 (n=126) | R/RE low vs saline KS p=8.3e-21; R/RE low vs DCZ KS p=1.2e-06; R/RE low vs CNO KS p=1.0e-07; DCZ vs saline p=8.4e-07, CNO vs saline p=4.1e-04
   middle: mean r R/RE low 0.399 (n=277), saline 0.798 (n=352), DCZ 0.664 (n=403), CNO 0.628 (n=301) | R/RE low vs saline KS p=8.2e-31; R/RE low vs DCZ KS p=6.7e-11; R/RE low vs CNO KS p=2.6e-08; DCZ vs saline p=1.5e-10, CNO vs saline p=1.0e-08
   end   : mean r R/RE low 0.305 (n=210), saline 0.735 (n=187), DCZ 0.654 (n=281), CNO 0.673 (n=214) | R/RE low vs saline KS p=2.1e-25; R/RE low vs DCZ KS p=1.9e-17; R/RE low vs CNO KS p=4.2e-17; DCZ vs saline p=2.4e-02, CNO vs saline p=8.0e-02
--- L2, RE low engaged laps
   place cells: {'R/RE low': (610, 'outside'), 'saline': (676, 'outside'), 'DCZ': (842, 'outside'), 'CNO': (641, 'outside')} | mean r: {'R/RE low': np.float64(0.345), 'saline': np.float64(0.793), 'DCZ': np.float64(0.657), 'CNO': np.float64(0.653)}
   DCZ vs saline overall: mean 0.657 vs 0.793, KS D=0.220 p=1.87e-16
   CNO vs saline overall: mean 0.653 vs 0.793, KS D=0.202 p=3.32e-12
   start : mean r R/RE low 0.323 (n=110), saline 0.860 (n=137), DCZ 0.643 (n=157), CNO 0.679 (n=126) | R/RE low vs saline KS p=7.0e-21; R/RE low vs DCZ KS p=1.5e-06; R/RE low vs CNO KS p=2.2e-08; DCZ vs saline p=8.4e-07, CNO vs saline p=4.1e-04
   middle: mean r R/RE low 0.391 (n=261), saline 0.798 (n=352), DCZ 0.664 (n=403), CNO 0.628 (n=301) | R/RE low vs saline KS p=4.9e-29; R/RE low vs DCZ KS p=3.5e-11; R/RE low vs CNO KS p=4.1e-08; DCZ vs saline p=1.5e-10, CNO vs saline p=1.0e-08
   end   : mean r R/RE low 0.296 (n=202), saline 0.735 (n=187), DCZ 0.654 (n=281), CNO 0.673 (n=214) | R/RE low vs saline KS p=1.4e-24; R/RE low vs DCZ KS p=1.6e-18; R/RE low vs CNO KS p=5.1e-17; DCZ vs saline p=2.4e-02, CNO vs saline p=8.0e-02
--- L2, RE low all laps
   place cells: {'R/RE low': (610, 'outside'), 'saline': (676, 'outside'), 'DCZ': (842, 'outside'), 'CNO': (641, 'outside')} | mean r: {'R/RE low': np.float64(0.348), 'saline': np.float64(0.793), 'DCZ': np.float64(0.657), 'CNO': np.float64(0.653)}
   DCZ vs saline overall: mean 0.657 vs 0.793, KS D=0.220 p=1.87e-16
   CNO vs saline overall: mean 0.653 vs 0.793, KS D=0.202 p=3.32e-12
   start : mean r R/RE low 0.362 (n=114), saline 0.860 (n=137), DCZ 0.643 (n=157), CNO 0.679 (n=126) | R/RE low vs saline KS p=8.3e-21; R/RE low vs DCZ KS p=4.0e-06; R/RE low vs CNO KS p=1.0e-07; DCZ vs saline p=8.4e-07, CNO vs saline p=4.1e-04
   middle: mean r R/RE low 0.383 (n=275), saline 0.798 (n=352), DCZ 0.664 (n=403), CNO 0.628 (n=301) | R/RE low vs saline KS p=7.4e-31; R/RE low vs DCZ KS p=5.5e-13; R/RE low vs CNO KS p=1.0e-09; DCZ vs saline p=1.5e-10, CNO vs saline p=1.0e-08
   end   : mean r R/RE low 0.294 (n=210), saline 0.735 (n=187), DCZ 0.654 (n=281), CNO 0.673 (n=214) | R/RE low vs saline KS p=1.1e-23; R/RE low vs DCZ KS p=8.4e-18; R/RE low vs CNO KS p=1.0e-16; DCZ vs saline p=2.4e-02, CNO vs saline p=8.0e-02
--- L3, RE low engaged laps
   place cells: {'R/RE low': (610, 'outside'), 'saline': (676, 'outside'), 'DCZ': (842, 'outside'), 'CNO': (641, 'outside')} | mean r: {'R/RE low': np.float64(0.359), 'saline': np.float64(0.793), 'DCZ': np.float64(0.657), 'CNO': np.float64(0.653)}
   DCZ vs saline overall: mean 0.657 vs 0.793, KS D=0.220 p=1.87e-16
   CNO vs saline overall: mean 0.653 vs 0.793, KS D=0.202 p=3.32e-12
   start : mean r R/RE low 0.339 (n=103), saline 0.860 (n=137), DCZ 0.643 (n=157), CNO 0.679 (n=126) | R/RE low vs saline KS p=6.2e-19; R/RE low vs DCZ KS p=1.1e-05; R/RE low vs CNO KS p=1.8e-07; DCZ vs saline p=8.4e-07, CNO vs saline p=4.1e-04
   middle: mean r R/RE low 0.407 (n=250), saline 0.798 (n=352), DCZ 0.664 (n=403), CNO 0.628 (n=301) | R/RE low vs saline KS p=5.7e-27; R/RE low vs DCZ KS p=3.1e-10; R/RE low vs CNO KS p=2.4e-07; DCZ vs saline p=1.5e-10, CNO vs saline p=1.0e-08
   end   : mean r R/RE low 0.309 (n=195), saline 0.735 (n=187), DCZ 0.654 (n=281), CNO 0.673 (n=214) | R/RE low vs saline KS p=7.1e-25; R/RE low vs DCZ KS p=2.8e-18; R/RE low vs CNO KS p=3.0e-17; DCZ vs saline p=2.4e-02, CNO vs saline p=8.0e-02
--- L3, RE low all laps
   place cells: {'R/RE low': (610, 'outside'), 'saline': (676, 'outside'), 'DCZ': (842, 'outside'), 'CNO': (641, 'outside')} | mean r: {'R/RE low': np.float64(0.355), 'saline': np.float64(0.793), 'DCZ': np.float64(0.657), 'CNO': np.float64(0.653)}
   DCZ vs saline overall: mean 0.657 vs 0.793, KS D=0.220 p=1.87e-16
   CNO vs saline overall: mean 0.653 vs 0.793, KS D=0.202 p=3.32e-12
   start : mean r R/RE low 0.366 (n=112), saline 0.860 (n=137), DCZ 0.643 (n=157), CNO 0.679 (n=126) | R/RE low vs saline KS p=1.2e-19; R/RE low vs DCZ KS p=6.7e-06; R/RE low vs CNO KS p=1.8e-07; DCZ vs saline p=8.4e-07, CNO vs saline p=4.1e-04
   middle: mean r R/RE low 0.386 (n=273), saline 0.798 (n=352), DCZ 0.664 (n=403), CNO 0.628 (n=301) | R/RE low vs saline KS p=2.0e-30; R/RE low vs DCZ KS p=7.8e-12; R/RE low vs CNO KS p=5.6e-09; DCZ vs saline p=1.5e-10, CNO vs saline p=1.0e-08
   end   : mean r R/RE low 0.309 (n=207), saline 0.735 (n=187), DCZ 0.654 (n=281), CNO 0.673 (n=214) | R/RE low vs saline KS p=6.3e-24; R/RE low vs DCZ KS p=1.9e-16; R/RE low vs CNO KS p=1.6e-16; DCZ vs saline p=2.4e-02, CNO vs saline p=8.0e-02
per mouse mean r (L1, engaged):
       R/RE low  saline    DCZ    CNO
mouse                                
009       0.016   0.791  0.622  0.658
011       0.601   0.798  0.809  0.835
012       0.221   0.751  0.551  0.560
014       0.381   0.842  0.539  0.541

Result: partially reproduced. Spatial correlation falls more after DCZ (0.657) and CNO (0.653) than after saline (0.793; KS p = 1.9e-16 and 3.3e-12), and at every position saline stays above R/RE low and DCZ and CNO lie below saline, under every reading. The place cell counts (610, 676, 842 and 641) are outside the tolerances of 928, 1139, 1629 and 1498; the dandiset holds 4 of the 6 mice.

supp18-saline-slow-laps¶

Splitting saline data into fast (before) and slow (after) laps gives only a small drop in spatial correlation, much smaller than DCZ or CNO, and no change in reliability (0.51 against 0.45) or out/in ratio (0.09 against 0.09).

Percentiles are taken within each mouse, or over the laps of all 4 mice pooled (pooled thresholds printed below); both are run, as the criterion asks. The split correlation uses fields defined on the fast laps; the same fields against all after-saline laps are shown so that the drop can be read like for like.

In [35]:
for fast, slow in [("Sal_fast", "Sal_slow_a"), ("Sal_fast", "Sal_slow_b"), ("Sal_fast_pool", "Sal_slow_a_pool"), ("Sal_fast_pool", "Sal_slow_b_pool")]:
    sp_f = np.concatenate([K.lap_speeds(RES[s]["beh"]["conds"]["BeforeSaline"])[RES[s]["fields_laps"][fast]] for s in DR])
    sp_s = np.concatenate([K.lap_speeds(RES[s]["beh"]["conds"]["AfterSaline"])[RES[s]["fields_laps"][slow]] for s in DR])
    pf_f = pd.concat([fields_df(s, fast) for s in DR])
    pf_s = pd.concat([fields_df(s, slow) for s in DR])
    print(f"--- {fast} against {slow} ({'below' if '_a' in slow else 'above'} the 40th percentile, {'pooled over mice' if 'pool' in slow else 'within each mouse'})")
    if 'pool' in slow:
        print('   pooled thresholds (cm/s):', K.pooled_saline_speed_thresholds())
    print(f"   lap speed fast {np.nanmean(sp_f):.2f} (paper 47.30 +/- 2.61), after {np.nanmean(sp_s):.2f} (paper 35.03 +/- 1.57) cm/s")
    for p, ex in [("reliability", (0.51, 0.06, 0.45, 0.13)), ("out_in", (0.09, 0.05, 0.09, 0.05))]:
        for reading in ["per-mouse means", "pooled fields"]:
            if reading == "per-mouse means":
                a, b2 = mean_ci(pf_f.groupby("mouse")[p].mean()), mean_ci(pf_s.groupby("mouse")[p].mean())
            else:
                a, b2 = mean_ci(pf_f[p]), mean_ci(pf_s[p])
            print(f"   {p} {reading}: fast {a[0]:.3f} +/- {a[1]:.3f}, after {b2[0]:.3f} +/- {b2[1]:.3f}; within tolerance {abs(a[0] - ex[0]) <= ex[1] and abs(b2[0] - ex[2]) <= ex[3]}")
    corr_split = pd.concat([pair_corr(s, fast, "AfterSaline", RES[s]["fields_laps"][slow]) for s in DR if RES[s]["fields"].get(fast) and len(RES[s]["fields_laps"][slow])])["r"].mean()
    co = fig4f[("L1", "engaged")]
    drop_split = co["saline"]["r"].mean() - corr_split
    # the same fields (defined on the fast laps) against all after-saline laps, so that the split is compared like for like
    corr_fast_all = pd.concat([pair_corr(s, fast, "AfterSaline") for s in DR if RES[s]["fields"].get(fast)])["r"].mean()
    print(f"   fast-lap fields against all after-saline laps {corr_fast_all:.3f}; drop from it to the slow laps {corr_fast_all - corr_split:.3f}")
    drop_dcz = co["saline"]["r"].mean() - co["DCZ"]["r"].mean()
    drop_cno = co["saline"]["r"].mean() - co["CNO"]["r"].mean()
    print(f"   spatial correlation: saline pair {co['saline']['r'].mean():.3f}, fast vs slow split {corr_split:.3f}, DCZ pair {co['DCZ']['r'].mean():.3f}, CNO pair {co['CNO']['r'].mean():.3f}")
    print(f"   drop of the split {drop_split:.3f}; drop for DCZ {drop_dcz:.3f}, CNO {drop_cno:.3f}; split drop below half the DCZ drop: {drop_split < 0.5 * drop_dcz}")
    print("   fields: fast", len(pf_f), "after", len(pf_s), "| laps per mouse fast", [len(RES[s]["fields_laps"][fast]) for s in DR], "after", [len(RES[s]["fields_laps"][slow]) for s in DR])
--- Sal_fast against Sal_slow_a (below the 40th percentile, within each mouse)
   lap speed fast 30.63 (paper 47.30 +/- 2.61), after 21.17 (paper 35.03 +/- 1.57) cm/s
   reliability per-mouse means: fast 0.340 +/- 0.086, after 0.366 +/- 0.114; within tolerance False
   reliability pooled fields: fast 0.361 +/- 0.019, after 0.386 +/- 0.014; within tolerance False
   out_in per-mouse means: fast 0.066 +/- 0.041, after 0.076 +/- 0.059; within tolerance True
   out_in pooled fields: fast 0.077 +/- 0.008, after 0.090 +/- 0.006; within tolerance True
   fast-lap fields against all after-saline laps 0.662; drop from it to the slow laps 0.086
   spatial correlation: saline pair 0.793, fast vs slow split 0.576, DCZ pair 0.657, CNO pair 0.653
   drop of the split 0.217; drop for DCZ 0.136, CNO 0.140; split drop below half the DCZ drop: False
   fields: fast 210 after 463 | laps per mouse fast [11, 18, 15, 19] after [18, 26, 27, 25]
--- Sal_fast against Sal_slow_b (above the 40th percentile, within each mouse)
   lap speed fast 30.63 (paper 47.30 +/- 2.61), after 29.41 (paper 35.03 +/- 1.57) cm/s
   reliability per-mouse means: fast 0.340 +/- 0.086, after 0.433 +/- 0.084; within tolerance False
   reliability pooled fields: fast 0.361 +/- 0.019, after 0.440 +/- 0.015; within tolerance False
   out_in per-mouse means: fast 0.066 +/- 0.041, after 0.081 +/- 0.039; within tolerance True
   out_in pooled fields: fast 0.077 +/- 0.008, after 0.074 +/- 0.004; within tolerance True
   fast-lap fields against all after-saline laps 0.662; drop from it to the slow laps 0.010
   spatial correlation: saline pair 0.793, fast vs slow split 0.652, DCZ pair 0.657, CNO pair 0.653
   drop of the split 0.141; drop for DCZ 0.136, CNO 0.140; split drop below half the DCZ drop: False
   fields: fast 210 after 734 | laps per mouse fast [11, 18, 15, 19] after [26, 39, 41, 37]
--- Sal_fast_pool against Sal_slow_a_pool (below the 40th percentile, pooled over mice)
   pooled thresholds (cm/s): {('BeforeSaline', 60): 28.037383177570092, ('AfterSaline', 40): 24.390243902439025}
   lap speed fast 32.56 (paper 47.30 +/- 2.61), after 18.36 (paper 35.03 +/- 1.57) cm/s
   reliability per-mouse means: fast 0.351 +/- 0.237, after 0.400 +/- 0.246; within tolerance False
   reliability pooled fields: fast 0.438 +/- 0.027, after 0.410 +/- 0.016; within tolerance False
   out_in per-mouse means: fast 0.058 +/- 0.110, after 0.075 +/- 0.051; within tolerance True
   out_in pooled fields: fast 0.098 +/- 0.009, after 0.079 +/- 0.005; within tolerance True
   fast-lap fields against all after-saline laps 0.810; drop from it to the slow laps 0.538
   spatial correlation: saline pair 0.793, fast vs slow split 0.273, DCZ pair 0.657, CNO pair 0.653
   drop of the split 0.520; drop for DCZ 0.136, CNO 0.140; split drop below half the DCZ drop: False
   fields: fast 189 after 517 | laps per mouse fast [1, 18, 6, 37] after [40, 33, 22, 1]
--- Sal_fast_pool against Sal_slow_b_pool (above the 40th percentile, pooled over mice)
   pooled thresholds (cm/s): {('BeforeSaline', 60): 28.037383177570092, ('AfterSaline', 40): 24.390243902439025}
   lap speed fast 32.56 (paper 47.30 +/- 2.61), after 31.41 (paper 35.03 +/- 1.57) cm/s
   reliability per-mouse means: fast 0.351 +/- 0.237, after 0.436 +/- 0.228; within tolerance False
   reliability pooled fields: fast 0.438 +/- 0.027, after 0.400 +/- 0.015; within tolerance False
   out_in per-mouse means: fast 0.058 +/- 0.110, after 0.059 +/- 0.070; within tolerance True
   out_in pooled fields: fast 0.098 +/- 0.009, after 0.084 +/- 0.005; within tolerance True
   fast-lap fields against all after-saline laps 0.810; drop from it to the slow laps -0.001
   spatial correlation: saline pair 0.793, fast vs slow split 0.812, DCZ pair 0.657, CNO pair 0.653
   drop of the split -0.019; drop for DCZ 0.136, CNO 0.140; split drop below half the DCZ drop: True
   fields: fast 189 after 538 | laps per mouse fast [1, 18, 6, 37] after [3, 32, 45, 63]

Result: not reproduced. Within each mouse, the fast laps run at 30.63 cm/s (paper 47.30) and the slow laps at 21.17 cm/s (reading a; paper 35.03); with pooled percentiles 32.56 and 18.36 cm/s. Reliability is outside tolerance under all four splits (fast 0.340 to 0.438 against 0.51 +/- 0.06). The drop in spatial correlation for the split exceeds half the DCZ drop of 0.136 (0.068) under three splits (0.217, 0.141, 0.520) and is below it only for the pooled reading (b) (-0.019), where the fast laps come almost all from 2 mice. Measured from the same fast-lap fields against all after-saline laps, the drop to the slow laps is 0.086 (reading a) and 0.010 (reading b) within each mouse.

fig4h-dreadd-reliability-outin¶

DCZ and CNO lower place field reliability and raise out/in field firing, as RE low does, while saline does not.

In [36]:
EXP4H = {"reliability": {"Rewarded": (0.41, 0.11), "RE low": (0.29, 0.12), "BeforeDCZ": (0.48, 0.10), "AfterDCZ": (0.41, 0.10), "BeforeCNO": (0.42, 0.11),
                         "AfterCNO": (0.35, 0.12), "BeforeSaline": (0.45, 0.11), "AfterSaline": (0.46, 0.08)},
         "out_in": {"Rewarded": (0.10, 0.04), "RE low": (0.17, 0.12), "BeforeDCZ": (0.10, 0.05), "AfterDCZ": (0.12, 0.04), "BeforeCNO": (0.10, 0.05),
                    "AfterCNO": (0.12, 0.05), "BeforeSaline": (0.09, 0.05), "AfterSaline": (0.09, 0.05)}}

def cdf_bins_test(a, b2, nb=20):
    edges = np.linspace(min(a.min(), b2.min()), max(a.max(), b2.max()), nb + 1)
    ca = np.cumsum(np.histogram(a, edges)[0]) / len(a)
    cb = np.cumsum(np.histogram(b2, edges)[0]) / len(b2)
    return stats.ttest_rel(ca, cb).pvalue

for L in K.LICK_READINGS:
    for lowset in ["engaged", "all"]:
        name = f"RElow_eng_{L}" if lowset == "engaged" else f"RElow_all_{L}"
        pf = {n: pd.concat([fields_df(s, n) for s in DR]) for n in ["Rewarded", "BeforeDCZ", "AfterDCZ", "BeforeCNO", "AfterCNO", "BeforeSaline", "AfterSaline"]}
        pf["RE low"] = pd.concat([fields_df(s, name) for s in DR])
        for reading in ["per-mouse means", "pooled fields"]:
            n_in = 0
            vals = {}
            for p, ex in EXP4H.items():
                for n, (v, tol) in ex.items():
                    m = mean_ci(pf[n].groupby("mouse")[p].mean())[0] if reading == "per-mouse means" else pf[n][p].mean()
                    vals[(p, n)] = m
                    n_in += abs(m - v) <= tol
            dirs = {d: (vals[("reliability", "After" + d)] < vals[("reliability", "Before" + d)], vals[("out_in", "After" + d)] > vals[("out_in", "Before" + d)]) for d in ["DCZ", "CNO"]}
            print(f"{L} RE low {lowset}, {reading}: within tolerance {n_in} of 16; DCZ/CNO (reliability lower, out/in higher) after: {dirs}")
            print("    " + "; ".join(f"{p[:3]} {n} {m:.3f}" for (p, n), m in vals.items()))
for p in ["reliability", "out_in"]:
    for d in ["Saline", "DCZ", "CNO"]:
        bm = [fields_df(s, "Before" + d)[p].mean() for s in DR]
        am = [fields_df(s, "After" + d)[p].mean() for s in DR]
        t = stats.ttest_rel(bm, am)
        a = pd.concat([fields_df(s, "Before" + d) for s in DR])[p].dropna().values
        b2 = pd.concat([fields_df(s, "After" + d) for s in DR])[p].dropna().values
        print(f"{p} {d}: per-mouse before {np.round(bm, 3).tolist()} after {np.round(am, 3).tolist()}; paired t over 4 mice p={t.pvalue:.3f}; paired t over cumulative histogram bins p={cdf_bins_test(a, b2):.3g}")
L1 RE low engaged, per-mouse means: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (True, True), 'CNO': (False, True)}
    rel Rewarded 0.370; rel RE low 0.276; rel BeforeDCZ 0.452; rel AfterDCZ 0.438; rel BeforeCNO 0.414; rel AfterCNO 0.417; rel BeforeSaline 0.384; rel AfterSaline 0.449; out Rewarded 0.097; out RE low 0.081; out BeforeDCZ 0.083; out AfterDCZ 0.089; out BeforeCNO 0.081; out AfterCNO 0.087; out BeforeSaline 0.091; out AfterSaline 0.081
L1 RE low engaged, pooled fields: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (np.True_, np.True_), 'CNO': (np.True_, np.True_)}
    rel Rewarded 0.382; rel RE low 0.300; rel BeforeDCZ 0.471; rel AfterDCZ 0.430; rel BeforeCNO 0.429; rel AfterCNO 0.417; rel BeforeSaline 0.392; rel AfterSaline 0.450; out Rewarded 0.093; out RE low 0.061; out BeforeDCZ 0.078; out AfterDCZ 0.090; out BeforeCNO 0.074; out AfterCNO 0.087; out BeforeSaline 0.096; out AfterSaline 0.076
L1 RE low all, per-mouse means: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (True, True), 'CNO': (False, True)}
    rel Rewarded 0.370; rel RE low 0.254; rel BeforeDCZ 0.452; rel AfterDCZ 0.438; rel BeforeCNO 0.414; rel AfterCNO 0.417; rel BeforeSaline 0.384; rel AfterSaline 0.449; out Rewarded 0.097; out RE low 0.101; out BeforeDCZ 0.083; out AfterDCZ 0.089; out BeforeCNO 0.081; out AfterCNO 0.087; out BeforeSaline 0.091; out AfterSaline 0.081
L1 RE low all, pooled fields: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (np.True_, np.True_), 'CNO': (np.True_, np.True_)}
    rel Rewarded 0.382; rel RE low 0.287; rel BeforeDCZ 0.471; rel AfterDCZ 0.430; rel BeforeCNO 0.429; rel AfterCNO 0.417; rel BeforeSaline 0.392; rel AfterSaline 0.450; out Rewarded 0.093; out RE low 0.086; out BeforeDCZ 0.078; out AfterDCZ 0.090; out BeforeCNO 0.074; out AfterCNO 0.087; out BeforeSaline 0.096; out AfterSaline 0.076
L2 RE low engaged, per-mouse means: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (True, True), 'CNO': (False, True)}
    rel Rewarded 0.370; rel RE low 0.224; rel BeforeDCZ 0.452; rel AfterDCZ 0.438; rel BeforeCNO 0.414; rel AfterCNO 0.417; rel BeforeSaline 0.384; rel AfterSaline 0.449; out Rewarded 0.097; out RE low 0.073; out BeforeDCZ 0.083; out AfterDCZ 0.089; out BeforeCNO 0.081; out AfterCNO 0.087; out BeforeSaline 0.091; out AfterSaline 0.081
L2 RE low engaged, pooled fields: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (np.True_, np.True_), 'CNO': (np.True_, np.True_)}
    rel Rewarded 0.382; rel RE low 0.250; rel BeforeDCZ 0.471; rel AfterDCZ 0.430; rel BeforeCNO 0.429; rel AfterCNO 0.417; rel BeforeSaline 0.392; rel AfterSaline 0.450; out Rewarded 0.093; out RE low 0.080; out BeforeDCZ 0.078; out AfterDCZ 0.090; out BeforeCNO 0.074; out AfterCNO 0.087; out BeforeSaline 0.096; out AfterSaline 0.076
L2 RE low all, per-mouse means: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (True, True), 'CNO': (False, True)}
    rel Rewarded 0.370; rel RE low 0.231; rel BeforeDCZ 0.452; rel AfterDCZ 0.438; rel BeforeCNO 0.414; rel AfterCNO 0.417; rel BeforeSaline 0.384; rel AfterSaline 0.449; out Rewarded 0.097; out RE low 0.092; out BeforeDCZ 0.083; out AfterDCZ 0.089; out BeforeCNO 0.081; out AfterCNO 0.087; out BeforeSaline 0.091; out AfterSaline 0.081
L2 RE low all, pooled fields: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (np.True_, np.True_), 'CNO': (np.True_, np.True_)}
    rel Rewarded 0.382; rel RE low 0.281; rel BeforeDCZ 0.471; rel AfterDCZ 0.430; rel BeforeCNO 0.429; rel AfterCNO 0.417; rel BeforeSaline 0.392; rel AfterSaline 0.450; out Rewarded 0.093; out RE low 0.100; out BeforeDCZ 0.078; out AfterDCZ 0.090; out BeforeCNO 0.074; out AfterCNO 0.087; out BeforeSaline 0.096; out AfterSaline 0.076
L3 RE low engaged, per-mouse means: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (True, True), 'CNO': (False, True)}
    rel Rewarded 0.370; rel RE low 0.237; rel BeforeDCZ 0.452; rel AfterDCZ 0.438; rel BeforeCNO 0.414; rel AfterCNO 0.417; rel BeforeSaline 0.384; rel AfterSaline 0.449; out Rewarded 0.097; out RE low 0.072; out BeforeDCZ 0.083; out AfterDCZ 0.089; out BeforeCNO 0.081; out AfterCNO 0.087; out BeforeSaline 0.091; out AfterSaline 0.081
L3 RE low engaged, pooled fields: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (np.True_, np.True_), 'CNO': (np.True_, np.True_)}
    rel Rewarded 0.382; rel RE low 0.244; rel BeforeDCZ 0.471; rel AfterDCZ 0.430; rel BeforeCNO 0.429; rel AfterCNO 0.417; rel BeforeSaline 0.392; rel AfterSaline 0.450; out Rewarded 0.093; out RE low 0.083; out BeforeDCZ 0.078; out AfterDCZ 0.090; out BeforeCNO 0.074; out AfterCNO 0.087; out BeforeSaline 0.096; out AfterSaline 0.076
L3 RE low all, per-mouse means: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (True, True), 'CNO': (False, True)}
    rel Rewarded 0.370; rel RE low 0.241; rel BeforeDCZ 0.452; rel AfterDCZ 0.438; rel BeforeCNO 0.414; rel AfterCNO 0.417; rel BeforeSaline 0.384; rel AfterSaline 0.449; out Rewarded 0.097; out RE low 0.095; out BeforeDCZ 0.083; out AfterDCZ 0.089; out BeforeCNO 0.081; out AfterCNO 0.087; out BeforeSaline 0.091; out AfterSaline 0.081
L3 RE low all, pooled fields: within tolerance 16 of 16; DCZ/CNO (reliability lower, out/in higher) after: {'DCZ': (np.True_, np.True_), 'CNO': (np.True_, np.True_)}
    rel Rewarded 0.382; rel RE low 0.274; rel BeforeDCZ 0.471; rel AfterDCZ 0.430; rel BeforeCNO 0.429; rel AfterCNO 0.417; rel BeforeSaline 0.392; rel AfterSaline 0.450; out Rewarded 0.093; out RE low 0.109; out BeforeDCZ 0.078; out AfterDCZ 0.090; out BeforeCNO 0.074; out AfterCNO 0.087; out BeforeSaline 0.096; out AfterSaline 0.076
reliability Saline: per-mouse before [0.428, 0.339, 0.363, 0.408] after [0.507, 0.486, 0.386, 0.414]; paired t over 4 mice p=0.138; paired t over cumulative histogram bins p=4.54e-06
reliability DCZ: per-mouse before [0.434, 0.565, 0.409, 0.401] after [0.504, 0.462, 0.377, 0.407]; paired t over 4 mice p=0.709; paired t over cumulative histogram bins p=7.15e-07
reliability CNO: per-mouse before [0.415, 0.476, 0.429, 0.338] after [0.431, 0.513, 0.324, 0.398]; paired t over 4 mice p=0.955; paired t over cumulative histogram bins p=0.0384
out_in Saline: per-mouse before [0.09, 0.056, 0.097, 0.122] after [0.074, 0.047, 0.089, 0.113]; paired t over 4 mice p=0.013; paired t over cumulative histogram bins p=7.99e-08
out_in DCZ: per-mouse before [0.075, 0.051, 0.088, 0.118] after [0.077, 0.067, 0.099, 0.112]; paired t over 4 mice p=0.315; paired t over cumulative histogram bins p=7.5e-05
out_in CNO: per-mouse before [0.078, 0.046, 0.077, 0.122] after [0.087, 0.046, 0.102, 0.112]; paired t over 4 mice p=0.499; paired t over cumulative histogram bins p=6.92e-06

Result: not reproduced. All 16 means are within tolerance under both readings. Out/in firing rises after DCZ and CNO, and reliability falls after DCZ (0.452 to 0.438 per mouse) but not after CNO (0.414 to 0.417) over per-mouse means; over pooled fields both fall. Saline is not without change: the out/in ratio falls after saline (paired t over 4 mice p = 0.013) and reliability differs over the cumulative histogram bins (p = 4.5e-06), so the absence of a saline effect, which the claim states, fails under both pairings.

fig4m-dreadd-overrepresentation¶

DCZ and CNO tend to reduce place field over-representation of the reward site compared with saline, but not significantly (P = 0.06), and less than RE low does.

In [37]:
EXP4M = {"BeforeSaline": 113, "AfterSaline": 114, "BeforeDCZ": 113, "AfterDCZ": 106, "BeforeCNO": 113, "AfterCNO": 109, "Rewarded": 110, "RE low": 103}
for L in K.LICK_READINGS:
    for lowset in ["engaged", "all"]:
        name = f"RElow_eng_{L}" if lowset == "engaged" else f"RElow_all_{L}"
        em = {}
        for s in DR:
            row = {n: end_minus_middle(fields_df(s, n)) for n in ["BeforeSaline", "AfterSaline", "BeforeDCZ", "AfterDCZ", "BeforeCNO", "AfterCNO", "Rewarded"]}
            row["RE low"] = end_minus_middle(fields_df(s, name))
            em[s] = row
        em = pd.DataFrame(em).T
        ch = pd.DataFrame({"saline": em["AfterSaline"] - em["BeforeSaline"], "DCZ": em["AfterDCZ"] - em["BeforeDCZ"],
                           "CNO": em["AfterCNO"] - em["BeforeCNO"], "RE low": em["RE low"] - em["Rewarded"]})
        t = stats.ttest_rel(ch["DCZ"], ch["saline"])
        w = stats.wilcoxon(ch["CNO"], ch["saline"])
        print(f"--- {L}, RE low {lowset}: change in end minus middle per mouse:\n{ch.round(1).to_string()}")
        print(f"    mean change saline {ch['saline'].mean():.1f}, DCZ {ch['DCZ'].mean():.1f}, CNO {ch['CNO'].mean():.1f}, RE low {ch['RE low'].mean():.1f}; "
              f"DCZ vs saline paired t p={t.pvalue:.3f}; CNO vs saline Wilcoxon p={w.pvalue:.3f}; |RE low| > |DCZ|: {abs(ch['RE low'].mean()) > abs(ch['DCZ'].mean())}")
pfc = {n: pd.concat([fields_df(s, n) for s in DR])["com"].dropna() for n in ["BeforeSaline", "AfterSaline", "BeforeDCZ", "AfterDCZ", "BeforeCNO", "AfterCNO", "Rewarded"]}
pfc["RE low"] = pd.concat([fields_df(s, "RElow_eng_L1") for s in DR])["com"].dropna().astype(float)
pfc["RE low (all laps)"] = pd.concat([fields_df(s, "RElow_all_L1") for s in DR])["com"].dropna().astype(float)
EXP4M["RE low (all laps)"] = EXP4M["RE low"]
fits = {n: stats.norm.fit(v) for n, v in pfc.items() if len(v) > 1}
print("fields per set:", {n: len(v) for n, v in pfc.items()})
print("Gaussian fits mean +/- SD (cm):", {n: tuple(np.round(f, 1)) for n, f in fits.items()}, "| within 10% of the paper's means:",
      {n: abs(f[0] - EXP4M[n]) <= 0.1 * EXP4M[n] for n, f in fits.items()})
--- L1, RE low engaged: change in end minus middle per mouse:
     saline   DCZ   CNO  RE low
009    11.2  14.3   8.9     NaN
011    -4.2  -8.7  -0.2    -5.7
012     5.1  -2.3   1.6    -9.5
014     5.2   5.0  12.7   -20.4
    mean change saline 4.3, DCZ 2.1, CNO 5.8, RE low -11.9; DCZ vs saline paired t p=0.402; CNO vs saline Wilcoxon p=0.625; |RE low| > |DCZ|: True
--- L1, RE low all: change in end minus middle per mouse:
     saline   DCZ   CNO  RE low
009    11.2  14.3   8.9    -3.6
011    -4.2  -8.7  -0.2    -2.2
012     5.1  -2.3   1.6   -13.8
014     5.2   5.0  12.7   -15.0
    mean change saline 4.3, DCZ 2.1, CNO 5.8, RE low -8.6; DCZ vs saline paired t p=0.402; CNO vs saline Wilcoxon p=0.625; |RE low| > |DCZ|: True
--- L2, RE low engaged: change in end minus middle per mouse:
     saline   DCZ   CNO  RE low
009    11.2  14.3   8.9     NaN
011    -4.2  -8.7  -0.2    -9.8
012     5.1  -2.3   1.6    -6.2
014     5.2   5.0  12.7    -4.5
    mean change saline 4.3, DCZ 2.1, CNO 5.8, RE low -6.8; DCZ vs saline paired t p=0.402; CNO vs saline Wilcoxon p=0.625; |RE low| > |DCZ|: True
--- L2, RE low all: change in end minus middle per mouse:
     saline   DCZ   CNO  RE low
009    11.2  14.3   8.9    -5.9
011    -4.2  -8.7  -0.2    -4.1
012     5.1  -2.3   1.6   -15.3
014     5.2   5.0  12.7   -13.6
    mean change saline 4.3, DCZ 2.1, CNO 5.8, RE low -9.7; DCZ vs saline paired t p=0.402; CNO vs saline Wilcoxon p=0.625; |RE low| > |DCZ|: True
--- L3, RE low engaged: change in end minus middle per mouse:
     saline   DCZ   CNO  RE low
009    11.2  14.3   8.9     NaN
011    -4.2  -8.7  -0.2    -1.2
012     5.1  -2.3   1.6     4.8
014     5.2   5.0  12.7    -6.8
    mean change saline 4.3, DCZ 2.1, CNO 5.8, RE low -1.1; DCZ vs saline paired t p=0.402; CNO vs saline Wilcoxon p=0.625; |RE low| > |DCZ|: False
--- L3, RE low all: change in end minus middle per mouse:
     saline   DCZ   CNO  RE low
009    11.2  14.3   8.9     4.9
011    -4.2  -8.7  -0.2   -17.6
012     5.1  -2.3   1.6   -15.3
014     5.2   5.0  12.7    -4.3
    mean change saline 4.3, DCZ 2.1, CNO 5.8, RE low -8.0; DCZ vs saline paired t p=0.402; CNO vs saline Wilcoxon p=0.625; |RE low| > |DCZ|: True
fields per set: {'BeforeSaline': 682, 'AfterSaline': 1169, 'BeforeDCZ': 847, 'AfterDCZ': 1183, 'BeforeCNO': 645, 'AfterCNO': 1178, 'Rewarded': 615, 'RE low': 240, 'RE low (all laps)': 371}
Gaussian fits mean +/- SD (cm): {'BeforeSaline': (np.float64(110.0), np.float64(55.1)), 'AfterSaline': (np.float64(110.8), np.float64(59.1)), 'BeforeDCZ': (np.float64(115.0), np.float64(56.8)), 'AfterDCZ': (np.float64(108.5), np.float64(58.3)), 'BeforeCNO': (np.float64(112.8), np.float64(58.9)), 'AfterCNO': (np.float64(113.9), np.float64(59.7)), 'Rewarded': (np.float64(113.8), np.float64(57.6)), 'RE low': (np.float64(111.3), np.float64(57.3)), 'RE low (all laps)': (np.float64(107.4), np.float64(56.4))} | within 10% of the paper's means: {'BeforeSaline': np.True_, 'AfterSaline': np.True_, 'BeforeDCZ': np.True_, 'AfterDCZ': np.True_, 'BeforeCNO': np.True_, 'AfterCNO': np.True_, 'Rewarded': np.True_, 'RE low': np.True_, 'RE low (all laps)': np.True_}

Result: reproduced. The change in end minus middle field percentage is 2.1 points after DCZ against 4.3 after saline (paired t p = 0.402, not significant), and the RE low change (-11.9 under L1, engaged laps, 3 mice; 009 has 2 engaged RE low laps) is larger in size; CNO against saline Wilcoxon p = 0.625. DCZ does not reduce the over-representation in absolute terms here; it rises less than after saline, and CNO (5.8) rises more than saline. The test has 4 mice. All Gaussian means are within 10% of the paper's.

Blocked claims¶

  • supp2-decoder-stable-control and controls-no-change-over-time: the control mice that stayed in R for the whole session are not in the dandiset (its 14 files are the 10 extinction mice and the 4 DREADD mice).
  • supp6-pupil-engagement: no pupil video or pupil traces are in the files (every HDF5 object of every file is listed in the data check; processing/ophys/ and analysis/ are empty).
  • supp15-tdtomato-controls: the three DAT-Cre tdTomato mice are not in the dandiset; all four DREADD files are described in full as "Dat-Cre mice injected with inhibitory DREADD receptor in VTA-Dopmaine neurons" (data check).
  • fig5-da-axon-ramps, fig5k-ramp-slope-max, fig5l-ramps-return-rr, supp20-novel-axons, supp21-heterogeneous-axons: the VTA axon imaging is not in the dandiset.
  • supp14-reward-cells: the reward cell definition is "as described in ref. 24": a cell that "fired at the reward zone on the track (40 cm before reward) and around reward delivery (2 seconds before and after)". Neither the paper nor the claims file says what counts as firing there (a place field with its centre in the zone, a peak, a threshold on activity), and the criterion is not in the paper's code. Any threshold chosen here would be arbitrary, and the count of 43 cells is the claim's main number. The on-track part could be read from the place fields found here (R and novel fields with a COM in the last 40 cm, 5 mice), but the criterion also needs the within-R reading S1, which needs the control mice, and readings S1 and S2 must agree; it is not run.

Summary¶

Claim Outcome
session-lap-counts reproduced
lick-persistence-ur not reproduced
fig1d-decoder-drop-ur reproduced
fig1e-decoder-follows-licking reproduced
fig1f-decoder-r2 partially reproduced
fig1g-decoder-error-all-locations reproduced
supp4-lickstop-definition-robust reproduced
supp4-lapwise-r2 partially reproduced
supp2-decoder-stable-control blocked (data missing)
supp5-not-running-speed partially reproduced
approach-ratio not reproduced
engaged-lap-counts not reproduced
supp6b-decoder-engaged-laps reproduced
disengaged-laps-uniform reproduced
supp6-pupil-engagement blocked (data missing)
fig2a-pv-cluster-transition not reproduced
fig2c-spatial-correlation-drop partially reproduced
fig2d-remapping-all-locations partially reproduced
fig2e-place-field-fates not reproduced
fig3a-field-numbers-similar not reproduced
fig3b-reliability reproduced
fig3b-out-in-ratio not reproduced
fig3b-width-intensity not reproduced
fig3c-degradation-all-locations partially reproduced
supp11-all-mice-parameters not reproduced
fig3d-reward-overrepresentation not reproduced
reward-zone-field-turnover not reproduced
controls-no-change-over-time blocked (data missing)
supp13-novel-environment partially reproduced
supp14-reward-cells blocked (methods underspecified)
supp16-dreadd-running-speed not reproduced
fig4f-dreadd-spatial-correlation partially reproduced
supp18-saline-slow-laps not reproduced
fig4h-dreadd-reliability-outin not reproduced
fig4m-dreadd-overrepresentation reproduced
supp15-tdtomato-controls blocked (data missing)
fig5-da-axon-ramps, fig5k, fig5l, supp20, supp21 blocked (data missing)

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.

  • The dandiset holds 10 of the 12 extinction mice (5 of the 6 with RR and a novel session) and 4 of the 6 DREADD mice (4 of 5 with saline). Counts of laps, fields and cells are over these mice. Thresholds written as counts of mice are applied as the same fraction of the mice here.
  • The files hold raw fluorescence, not the paper's dF/F. dF/F here uses the 8th percentile in a 15 s window, and significant transients start above 2 and end below 0.5 robust SD and last 0.5 s or more (ref. 75; assumption). Place field candidates are bins above 25% of the way from baseline to peak (ref. 75), and the bootstrap uses 100 circular shifts. The fields found are wider than the paper's (70 to 90 cm in R against 53 cm); the candidate regions are 31.4 to 43.2 cm wide (per-mouse means), narrower than the paper's 53 to 61 cm, and the Methods width rule widens them.
  • The DREADD files carry a constant offset of about 17000 counts, removed as the file minimum before dF/F (assumption).
  • Position is in volts; the top of the range, where reward is delivered, is read as 200 cm. The 30 Hz clock agrees with the 1.5 s reward pause (46 to 49 frames), yet lap times are longer and speeds slower than the paper's (wild-type R laps 12.2 s against 7.34 s; DREADD lap speeds about 26 against 42 cm/s). Lap time does not depend on the volts-to-cm scale, so the difference lies in the data or in a definition of lap time the paper does not give.
  • The decoder treats the track as a circle by taking each prediction at its nearest copy of the true bin, as the criterion's "track treated as circular" says; without it R2 values are far lower (shown).
  • The approach ratio that defines engaged laps is taken over the traversal of the track, from the lap start to the track end, leaving out the pause after the reward (Methods, "velocity was calculated at each point along the 2 m track"; assumption); laps with an undefined ratio are neither engaged nor disengaged. With this split 46% of RE low laps are engaged and 2 of the 5 R-UR-RR mice have more than 12 engaged laps; the code's ratio, a variant, gives 67% and 4 mice. The whole-lap ratio of the first version took in the pause and is shown as a variant only.
  • The reward zone starts at the mean position of the first R lick before the reward and after 20 cm (licks just after the teleport belong to the previous reward; the 20 cm is an assumption).
  • The within-R reading S1 (control mice) of the spatial correlation could not be run, so the claims that need S1 and S2 to agree are partially reproduced at most.

What was found about the dataset.

  • sub-014 has 6000 fewer behaviour rows than fluorescence frames; decoding shows the missing rows are at the start of BeforeCNO, so every later behaviour row is 6000 frames early. sub-NR6 has 8 extra behaviour rows and sub-CFC4 one row fewer, at the end.
  • The DREADD fluorescence carries an undocumented offset of about 17000 counts.
  • In NR6 and NR14 a reward is delivered at the end of the first UR lap as the frame counts define the conditions.
  • Conditions are not marked in the files except as frame counts in a free-text description; the position channel has no unit or scale; the files do not say which mice the paper's figures use.
  • The control mice, the dark sessions, the pupil video, the tdTomato mice and the VTA axon imaging are not in the dandiset, and 2 extinction mice and 2 DREADD mice are missing.