Talbot et al. (2018) on CRCNS hc-16: single cells, coordination and frame-specific firing¶
Paper: Talbot, Sparks, Dvorak, Curran, Alarcon and Fenton (2018), "Normal CA1 Place Fields but Discoordinated Network Discharge in a Fmr1-Null Mouse Model of Fragile X Syndrome", Neuron 97:684-697, doi:10.1016/j.neuron.2017.12.043.
Dataset: CRCNS hc-16 (Sparks, Talbot, Dvorak and Fenton), doi:10.6080/K01V5C44, read from the
public bucket crcnsarchive at hc-16/live/data.zip, streamed with fsspec and zipfile.
Version: CRCNS data sets are not versioned, so the version is recorded as unversioned. The
paper cites the data set's DOI only (Key Resources Table), and the bucket holds one copy of the
data, under live/ (there is no old/), so that copy is the one read. The cell below reads the
bucket's checksums.md5; the file read is data.zip, MD5 45b534dd6dbeb94db539bc429223eecb
(data descriptor version 0.6, April 26, 2018). A later change of the zip would change that sum.
Claims: claims/crcns-hc-16/10.1016-j.neuron.2017.12.043.yaml, written from the version of
record and its supplement (Figures S1 to S6, Tables S1 and S2), as its opening comment says.
| Claim | Here |
|---|---|
| fig1ab-baseline-transmission, fig1cd-potentiation, figs1-avoidance-equal | blocked: slice recordings and slice-cohort behaviour not in the data set |
| unit-and-cell-counts | blocked: the data set holds 15 of the paper's 21 mice and no isolation measures (counts printed for the record) |
| interneuron-proportion | tested (rate and bursting only; no waveforms) |
| place-cell-proportion | tested |
| figs3-nonspatial-properties-similar | tested (no waveform width) |
| fig2-spatial-quality-similar | tested (large box only) |
| tables1-place-cell-properties-similar | tested |
| tables1-nonspatial-active-pixels | tested |
| figs4-remapping-similar | blocked: no box and cylinder recordings of the same cells (shown below) |
| fig3b-place-cell-modulation-similar, fig3bc-weaker-modulation-nonspatial-interneurons, fig3b-phase-preference, fig3d-stereotypy, figs5a-power-spectra-similar | blocked: no LFP in the data set |
| overdispersion-passes, overdispersion-cells | tested |
| fig4b-covariance-30-passes, fig4b-covariance-40-passes | tested |
| fig4cd-kendall-place-cells, fig4d-network-state-recurrence, kendall-nonspatial-pyramidal | tested |
| kendall-quiet-states | tested (low-speed periods; no sleep recordings) |
| locomotion-similar | tested |
| fig5b-frame-specific-ipos, fig5c-ensemble-coordination, figs6-room-frame-preference | tested |
| fig6-covariance-sem-predicts-errors, fig6-kendall-predicts-errors, discussion-single-cell-measures-no-relation, fig6-avoidance-behavior | blocked: avoidance sessions not in the data set |
Population used throughout: the data set has no isolation measures (IsoIBG, IsoINN) and no waveforms, so the paper's well-isolated cells cannot be selected. Every cluster other than cluster 0 of a tetrode is used. This is the largest difference from the paper's method and bears on every tested claim. Two variants bear on it: a refractory filter (clusters with at most 0.5% of interspike intervals below 2 ms), a proxy for isolation, and single-cell measures on the code's 32 x 32 grid, which needs no spatial scale.
import sys, time, hashlib, re
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import hc16
pd.set_option("display.width", 200)
pd.set_option("display.max_columns", 40)
pd.set_option("display.max_rows", 200)
T0 = time.time()
md5 = hc16.check_md5_list()
print("checksums.md5:", md5)
assert md5["data.zip"] == hc16.DATA_ZIP_MD5
z = hc16.open_zip()
files = hc16.list_members(z)
sessions = hc16.load_sessions(files)
S = {s.id: s for s in sessions}
print(f"loaded {len(sessions)} sessions in {time.time() - T0:.0f} s")
checksums.md5: {'data.zip': '45b534dd6dbeb94db539bc429223eecb', 'docs/crcns_hc-16_data_description.docx': '41bc3a8b453215e070ad5b925b3fe27b', 'docs/crcns_hc-16_data_description.pdf': '86544fa66ee16226829909e6ec996715', 'filelist.txt': '8d60384041e6f525496910b5e6b52fa2'}
loaded 41 sessions in 29 s
print("Zip members matched by name:", len(files), "of", sum(1 for i in z.infolist() if not i.is_dir()),
"(the rest are .DS_Store)")
print(files.groupby(["env", "genotype", "frame", "part", "times"]).size().rename("files").to_string())
sess = pd.DataFrame([dict(session=s.id, env=s.env, genotype=s.genotype, mouse=s.mouse, date=s.date,
cond=s.cond, cut=s.cut, rate_hz=round(s.rate, 3), duration_s=round(s.duration, 1),
frames="+".join(sorted(s.frames)), clusters=len(s.counts), cells=len(s.cells),
untracked=round(1 - s.tracked.mean(), 3),
extent_px=f"{s.extent_px[0]:.0f}x{s.extent_px[1]:.0f}",
px_per_cm=round(s.px_per_cm, 2), leftover_bytes=s.leftover)
for s in sessions])
print(sess.to_string())
print("\nOne full TS header (%s, first file):" % sessions[0].id)
print(sessions[0].header)
print("Header fields over every file read:", sorted(set().union(*[s.header_fields for s in sessions])))
print("SCALE_X and SCALE_Y values in the first file of each session:",
sorted({v for s in sessions for v in re.findall(r"%SCALE_[XY]\S*\s+([\d.]+)", s.header)}))
print("\nMice per genotype:", sess.groupby("genotype").mouse.nunique().to_dict())
print("Sessions per environment and genotype:\n", sess.groupby(["env", "genotype"]).size().unstack())
print("Position sampling rate (Hz): %.3f to %.3f; durations %.0f to %.0f s"
% (sess.rate_hz.min(), sess.rate_hz.max(), sess.duration_s.min(), sess.duration_s.max()))
# cluster 0 of each tetrode
c0 = [(s.id, u, s.counts[u].sum() / s.duration) for s in sessions for u in s.counts if u.endswith("C0")]
c0r = np.array([r for _, _, r in c0])
print(f"\nCluster 0: {len(c0)} files, rate median {np.median(c0r):.1f} AP/s (range {c0r.min():.1f} to {c0r.max():.1f})")
allr = np.array([s.counts[u].sum() / s.duration for s in sessions for u in s.cells])
print(f"Other clusters: {len(allr)}, rate median {np.median(allr):.2f} AP/s (range {allr.min():.3f} to {allr.max():.1f})")
# spikes are sorted, inside the session, and each lies near its position sample
bad_sort = 0; offs = []
for s in sessions:
for u in s.cells:
sp = s.spikes[u]
bad_sort += int(np.any(np.diff(sp) < 0))
k = np.repeat(np.arange(s.nsamp), s.counts[u])
offs.append(((sp - s.t0) / 1e4 * s.rate - k))
offs = np.concatenate(offs)
print(f"Clusters with unsorted spike times: {bad_sort}")
print("Spike time minus the nominal time of its position sample (t0 + k/rate), in samples: "
"percentiles 0/50/100 = %.2f / %.2f / %.2f" % tuple(np.percentile(offs, [0, 50, 100])))
res = []
for s in sessions:
for u in s.counts:
k = np.repeat(np.arange(s.nsamp), s.counts[u])
res.append((s.spikes[u] / 1e4 - s.sample_time[k]) * s.rate)
res = np.concatenate(res)
print("Spike time minus the estimated time of its sample (hc16.Session.sample_time), in samples: "
"percentiles 0.1/50/99.9 = %.2f / %.2f / %.2f" % tuple(np.percentile(res, [0.1, 50, 99.9])))
for s in sessions:
for u in s.cells:
d = np.diff(s.spikes[u])
if np.any(d < 0):
print(f" unsorted: {s.id} {u}: {int((d < 0).sum())} backward steps of {len(d) + 1} spikes, "
f"largest {-d.min() / 10:.1f} ms")
# x and y range (tracker pixels, 0 to 255; (0, 0) is untracked)
for env, g in sess.groupby("env"):
print(f"{env}: room-frame extent {', '.join(g.extent_px)} px; px/cm {g.px_per_cm.min()} to {g.px_per_cm.max()}")
Zip members matched by name: 4087 of 4090 (the rest are .DS_Store)
env genotype frame part times
big_box Fmr1-null Rm full False 175
True 175
pos full False 7
True 7
WT Rm _0-15min False 4
full False 139
True 139
pos _0-15min False 2
full False 8
True 8
rotating_cylinder Fmr1-null Ar _0-15min False 65
_15-30min False 40
full False 97
True 97
Rm _0-15min False 65
_15-30min False 40
full False 97
True 97
pos _0-15min False 3
_15-30min False 2
full False 4
True 4
WT Ar _0-15min False 94
_15-30min False 94
full False 94
True 94
Rm _0-15min False 94
_15-30min False 94
full False 94
True 94
pos _0-15min False 3
_15-30min False 3
full False 3
True 3
stationary_cylinder Fmr1-null Ar _0-15min False 43
_15-30min False 43
full False 43
True 43
Rm _0-15min False 221
_15-30min False 221
full False 285
True 285
pos _0-15min False 8
_15-30min False 8
full False 10
True 10
WT Rm _0-15min False 141
_15-30min False 141
full False 257
True 257
pos _0-15min False 7
_15-30min False 7
full False 9
True 9
session env genotype mouse date cond cut rate_hz duration_s frames clusters cells untracked extent_px px_per_cm leftover_bytes
0 M10_052214_blackbox big_box Fmr1-null M10 052214 blackbox cl120915 30.019 1798.6 Rm 6 5 0.020 216x220 4.36 0
1 M18_100814_blackbox big_box Fmr1-null M18 100814 blackbox cl120915 30.019 1798.4 Rm 19 16 0.060 186x182 3.68 0
2 M18_100914_rotating rotating_cylinder Fmr1-null M18 100914 rotating cl120815 30.009 1799.2 Ar+Rm 32 24 0.045 231x234 5.81 0
3 M18_100914_stationary1 stationary_cylinder Fmr1-null M18 100914 stationary1 cl120815 30.009 1799.1 Rm 32 24 0.049 232x234 5.83 0
4 M18_100914_stationary2 stationary_cylinder Fmr1-null M18 100914 stationary2 cl120815 30.009 1799.2 Rm 32 24 0.046 228x232 5.75 0
5 M29_061215_blackbox1 big_box Fmr1-null M29 061215 blackbox1 cl121115 30.019 1199.3 Rm 32 24 0.031 190x181 3.71 0
6 M29_112414_rotating rotating_cylinder Fmr1-null M29 112414 rotating cl020316 30.019 1798.0 Ar+Rm 25 17 0.038 220x225 5.56 0
7 M29_112414_stationary1 stationary_cylinder Fmr1-null M29 112414 stationary1 cl120715 30.019 1798.7 Ar+Rm 25 17 0.048 227x228 5.68 0
8 M29_112414_stationary2 stationary_cylinder Fmr1-null M29 112414 stationary2 cl020316 30.018 1798.7 Rm 25 17 0.027 226x227 5.67 0
9 M33_051715_blackbox big_box Fmr1-null M33 051715 blackbox cl121515 30.019 1798.8 Rm 25 18 0.050 186x181 3.67 0
10 M33_051815_rotating rotating_cylinder Fmr1-null M33 051815 rotating cl112715 30.009 1799.5 Ar+Rm 9 2 0.013 234x226 5.75 0
11 M33_051815_stationary1 stationary_cylinder Fmr1-null M33 051815 stationary1 cl112715 30.010 1799.4 Ar+Rm 9 2 0.023 234x233 5.84 0
12 M33_051815_stationary2 stationary_cylinder Fmr1-null M33 051815 stationary2 cl112715 30.009 1781.8 Ar+Rm 9 2 0.068 228x225 5.66 0
13 M34_041015_blackbox big_box Fmr1-null M34 041015 blackbox cl121615 30.019 1798.4 Rm 42 34 0.057 188x182 3.70 0
14 M34_041115_stationary1 stationary_cylinder Fmr1-null M34 041115 stationary1 cl101115 30.009 1798.7 Rm 46 38 0.044 235x235 5.88 0
15 M34_041115_stationary2 stationary_cylinder Fmr1-null M34 041115 stationary2 cl101115b 30.009 1798.9 Rm 46 38 0.040 234x234 5.85 0
16 M37_050815_blackbox big_box Fmr1-null M37 050815 blackbox cl121615 30.019 1798.9 Rm 34 26 0.059 186x180 3.66 0
17 M37_051115_rotating rotating_cylinder Fmr1-null M37 051115 rotating cl101015 30.009 1799.5 Ar+Rm 31 23 0.029 226x226 5.65 0
18 M37_051115_stationary1 stationary_cylinder Fmr1-null M37 051115 stationary1 cl101015 30.009 1799.5 Rm 30 22 0.030 228x231 5.74 0
19 M37_051115_stationary2 stationary_cylinder Fmr1-null M37 051115 stationary2 cl101015 30.009 1799.4 Rm 31 23 0.038 227x230 5.71 0
20 M9_052614_blackbox big_box Fmr1-null M9 052614 blackbox cl121515 30.020 1798.2 Rm 17 12 0.021 219x221 4.40 0
21 M1_040714_blackbox big_box WT M1 040714 blackbox cl120915 30.020 1798.3 Rm 10 9 0.004 188x207 3.95 0
22 M11_071414_blackbox big_box WT M11 071414 blackbox cl123115A 30.019 1798.6 Rm 10 8 0.032 218x222 4.40 0
23 M16_090414_rotating rotating_cylinder WT M16 090414 rotating cl101015 30.009 1799.1 Ar+Rm 58 50 0.084 235x235 5.88 0
24 M16_090414_stationary1 stationary_cylinder WT M16 090414 stationary1 cl101015 30.009 1799.1 Rm 58 50 0.051 231x233 5.80 0
25 M16_090414_stationary2 stationary_cylinder WT M16 090414 stationary2 cl101015 30.009 1799.0 Rm 58 50 0.046 232x232 5.80 0
26 M16_091214_blackbox big_box WT M16 091214 blackbox cl123115 30.019 1198.9 Rm 35 27 0.013 184x180 3.64 0
27 M28_032515_rotating rotating_cylinder WT M28 032515 rotating cl020216 30.011 1792.2 Ar+Rm 13 10 0.116 231x230 5.76 0
28 M28_032515_stationary1 stationary_cylinder WT M28 032515 stationary1 cl020216 30.009 1799.0 Rm 13 10 0.129 222x225 5.59 0
29 M28_032515_stationary2 stationary_cylinder WT M28 032515 stationary2 cl020216 30.009 1798.8 Rm 18 10 0.139 227x229 5.69 0
30 M28_060315_blackbox1 big_box WT M28 060315 blackbox1 cl123115 30.019 1198.4 Rm 15 14 0.050 187x179 3.66 0
31 M3_041314_blackbox big_box WT M3 041314 blackbox cl123115 30.019 1798.3 Rm 5 3 0.026 177x192 3.69 0
32 M32_041715_rotating rotating_cylinder WT M32 041715 rotating cl101015 30.008 1799.0 Ar+Rm 23 15 0.049 223x229 5.65 0
33 M32_041715_stationary1 stationary_cylinder WT M32 041715 stationary1 cl101015 30.009 1799.2 Rm 23 15 0.053 223x222 5.56 0
34 M32_041715_stationary2 stationary_cylinder WT M32 041715 stationary2 cl101015 30.009 1799.1 Rm 23 15 0.043 214x223 5.47 0
35 M32_060315_blackbox1 big_box WT M32 060315 blackbox1 cl123115 30.018 1198.6 Rm 32 24 0.048 182x178 3.60 0
36 M35_040115_blackbox big_box WT M35 040115 blackbox cl123115 30.019 1798.7 Rm 23 15 0.012 189x180 3.69 0
37 M35_040215_stationary1 stationary_cylinder WT M35 040215 stationary1 cl100915 30.009 1799.1 Rm 21 13 0.028 227x230 5.71 0
38 M35_040215_stationary2 stationary_cylinder WT M35 040215 stationary2 cl100915 30.009 1798.4 Rm 21 13 0.020 224x226 5.62 0
39 M36_041415_blackbox big_box WT M36 041415 blackbox cl123115 30.019 1798.6 Rm 9 1 0.022 185x181 3.66 0
40 M36_050115_stationary1 stationary_cylinder WT M36 050115 stationary1 cl120215 30.009 1799.0 Rm 22 14 0.026 226x228 5.67 0
One full TS header (M10_052214_blackbox, first file):
13
%%BEGIN_HEADER
%SAMPLING_INTERVAL(samps/sec) 30.019213
%FIRST_TIMESTAMP(100usUnits) 7430
%SCALE_Y(RatioTracktoMapPixels) 1.00
%SCALE_X(RatioTracktoMapPixels) 1.00
TIME_SERIES FORMAT:
Ascii header:
NumberOfLinesInHeader
(NumberOfLinesInHeader - 1) of Ascii Info.
Binary data format:
Xposition:1byte Yposition:1byte NspksInSample:1byte TimestampOfSpk-1:4bytes...TimeStampOfSpk-n:4bytes
%%ENDHEADER
Header fields over every file read: ['BEGIN_HEADER', 'ENDHEADER', 'FIRST_TIMESTAMP', 'SAMPLING_INTERVAL', 'SCALE_X', 'SCALE_Y']
SCALE_X and SCALE_Y values in the first file of each session: ['1.00']
Mice per genotype: {'Fmr1-null': 7, 'WT': 8}
Sessions per environment and genotype:
genotype Fmr1-null WT
env
big_box 7 8
rotating_cylinder 4 3
stationary_cylinder 10 9
Position sampling rate (Hz): 30.008 to 30.020; durations 1198 to 1800 s
Cluster 0: 273 files, rate median 25.9 AP/s (range 0.0 to 184.7)
Other clusters: 774, rate median 1.39 AP/s (range 0.001 to 52.2)
Clusters with unsorted spike times: 6 Spike time minus the nominal time of its position sample (t0 + k/rate), in samples: percentiles 0/50/100 = -0.85 / 9.54 / 118.20
Spike time minus the estimated time of its sample (hc16.Session.sample_time), in samples: percentiles 0.1/50/99.9 = -0.01 / 0.50 / 1.01 unsorted: M34_041015_blackbox P0C2: 1 backward steps of 21439 spikes, largest 830.1 ms unsorted: M34_041015_blackbox P2C2: 1 backward steps of 6603 spikes, largest 217.9 ms unsorted: M37_051115_stationary1 P2C2: 1 backward steps of 50269 spikes, largest 1441.8 ms unsorted: M37_051115_stationary1 P8C2: 1 backward steps of 88956 spikes, largest 1129.8 ms unsorted: M16_090414_rotating P1C1: 1 backward steps of 35444 spikes, largest 604.9 ms unsorted: M35_040215_stationary2 P0C2: 1 backward steps of 47283 spikes, largest 399.9 ms big_box: room-frame extent 216x220, 186x182, 190x181, 186x181, 188x182, 186x180, 219x221, 188x207, 218x222, 184x180, 187x179, 177x192, 182x178, 189x180, 185x181 px; px/cm 3.6 to 4.4 rotating_cylinder: room-frame extent 231x234, 220x225, 234x226, 226x226, 235x235, 231x230, 223x229 px; px/cm 5.56 to 5.88 stationary_cylinder: room-frame extent 232x234, 228x232, 227x228, 226x227, 234x233, 228x225, 235x235, 234x234, 228x231, 227x230, 231x233, 232x232, 222x225, 227x229, 223x222, 214x223, 227x230, 224x226, 226x228 px; px/cm 5.47 to 5.88
Checks of the data against the description, and of the files against each other:
- The descriptor gives x and y in 0-255 pixels and times in 100 us units, but no spatial scale. The scale is taken from the extent of the tracked room-frame positions (0.1 to 99.9 percentiles) and the enclosure sizes of the STAR Methods (large box 50 cm, arena 40 cm). This is an assumption; the printed px/cm differ between box sessions (about 3.6 and 4.4), which fits two camera set-ups and is consistent with, not proof of, the assumed sizes.
- The TS headers hold the sampling rate, the first timestamp and a track-to-map scale of 1.00; no isolation measure and no spatial scale in cm (printed above).
- The descriptor's example header has a sampling interval of 58.3 samples/s; every file here has 30.0, matching the 30 frames/s of the STAR Methods.
- The header's sampling rate and first timestamp do not give the times of the position samples: spikes lie up to 118 samples (about 4 s) after the nominal time t0 + k/rate of the sample they are filed under, as the rate drifts against the spike clock and frames are dropped. The helper estimates each sample's time from the spikes filed under it; after that the spikes lie 0 to 1 sample after their sample's time, as they should. Six spike trains have one backward step in their times (printed above).
- The next cells check that the
.timesfiles repeat the spike times of the TS files, that the_0-15minand_15-30minfiles are parts of the full sessions (one position sample at the split is in neither part, so a spike filed under it is lost), that the arena-frame files of stationary sessions repeat the room frame, and whether any recording is deposited twice.
rng = np.random.default_rng(0)
# .times files against the TS spikes, for every cell of three sessions
chk = []
for sid in ["M16_090414_stationary1", "M34_041015_blackbox", "M37_051115_rotating"]:
s = S[sid]
for u in s.cells:
nm = files[(files.mouse == s.mouse) & (files.date == s.date) & (files.cond == s.cond) & (files.unit == u)
& (files.frame == "Rm") & (files.part == "full") & files.times].name.iloc[0]
v = np.array(z.read(nm).split(), dtype=np.int64)
chk.append(np.array_equal(v, s.spikes[u]))
print(f".times files identical to the TS spike times: {sum(chk)} of {len(chk)} cells (3 sessions)")
# 0-15 and 15-30 min parts against the full session
s = S["M16_090414_rotating"]
part = hc16.read_members([f"data/rotating_cylinder/WT/M16_090414_rotating-cl101015-fix_0-15min.{u}Rm" for u in s.cells[:5]]
+ [f"data/rotating_cylinder/WT/M16_090414_rotating-cl101015-fix_15-30min.{u}Rm" for u in s.cells[:5]])
for u in s.cells[:5]:
a = part[f"data/rotating_cylinder/WT/M16_090414_rotating-cl101015-fix_0-15min.{u}Rm"]
b = part[f"data/rotating_cylinder/WT/M16_090414_rotating-cl101015-fix_15-30min.{u}Rm"]
joined = np.r_[a["spk"], b["spk"]]
print(f"{u}: 0-15 min {len(a['x'])} samples, 15-30 min {len(b['x'])}, full {s.nsamp}; "
f"spikes {len(a['spk'])}+{len(b['spk'])} vs {len(s.spikes[u])}, "
f"parts equal to the full spike train: {np.array_equal(joined, s.spikes[u])}")
na, nb_ = len(a["x"]), len(b["x"])
print("First part equals the full file's first samples:", np.array_equal(a["x"], s.frames["Rm"][0][:na]),
"; second part equals its last samples:", np.array_equal(b["x"], s.frames["Rm"][0][s.nsamp - nb_:]))
dropped = np.arange(na, s.nsamp - nb_)
print("Samples of the full file in neither part:", dropped.tolist(),
"; spikes there per cell:", {u: int(s.counts[u][dropped].sum()) for u in s.cells[:5]})
# arena frame of stationary sessions
for s in sessions:
if "Ar" in s.frames and s.env == "stationary_cylinder":
print(f"{s.id}: arena-frame positions identical to room frame: "
f"{np.array_equal(s.frames['Ar'][0], s.frames['Rm'][0]) and np.array_equal(s.frames['Ar'][1], s.frames['Rm'][1])}")
for s in sessions:
if s.env == "rotating_cylinder":
same = np.mean((s.frames["Ar"][0] == s.frames["Rm"][0]) & (s.frames["Ar"][1] == s.frames["Rm"][1]))
print(f"{s.id}: fraction of samples with equal room and arena positions {same:.3f}")
# recordings deposited twice: identical spike trains or identical position series across sessions
h_spk, h_pos = {}, {}
for s in sessions:
h_pos.setdefault(hashlib.md5(s.frames["Rm"][0].tobytes() + s.frames["Rm"][1].tobytes()).hexdigest(), []).append(s.id)
for u in s.cells:
if len(s.spikes[u]) > 10:
h_spk.setdefault(hashlib.md5(s.spikes[u].tobytes()).hexdigest(), []).append(f"{s.id}.{u}")
dup_pos = [v for v in h_pos.values() if len(v) > 1]
dup_spk = [v for v in h_spk.values() if len(v) > 1]
dup_spk_x = [v for v in dup_spk if len({x.split('.')[0] for x in v}) > 1]
print(f"Position series shared by more than one session: {dup_pos}")
print(f"Identical spike trains across sessions: {len(dup_spk_x)}; within a session: {len(dup_spk) - len(dup_spk_x)}")
print(dup_spk[:10])
.times files identical to the TS spike times: 107 of 107 cells (3 sessions)
P0C1: 0-15 min 26981 samples, 15-30 min 27007, full 53989; spikes 20173+17268 vs 37441, parts equal to the full spike train: True
P0C2: 0-15 min 26981 samples, 15-30 min 27007, full 53989; spikes 12203+11069 vs 23273, parts equal to the full spike train: False
P0C3: 0-15 min 26981 samples, 15-30 min 27007, full 53989; spikes 587+558 vs 1145, parts equal to the full spike train: True
P0C4: 0-15 min 26981 samples, 15-30 min 27007, full 53989; spikes 650+483 vs 1133, parts equal to the full spike train: True
P0C5: 0-15 min 26981 samples, 15-30 min 27007, full 53989; spikes 743+634 vs 1377, parts equal to the full spike train: True
First part equals the full file's first samples: True ; second part equals its last samples: True
Samples of the full file in neither part: [26981] ; spikes there per cell: {'P0C1': 0, 'P0C2': 1, 'P0C3': 0, 'P0C4': 0, 'P0C5': 0}
M29_112414_stationary1: arena-frame positions identical to room frame: True
M33_051815_stationary1: arena-frame positions identical to room frame: True
M33_051815_stationary2: arena-frame positions identical to room frame: True
M18_100914_rotating: fraction of samples with equal room and arena positions 0.048
M29_112414_rotating: fraction of samples with equal room and arena positions 0.041
M33_051815_rotating: fraction of samples with equal room and arena positions 0.015
M37_051115_rotating: fraction of samples with equal room and arena positions 0.030
M16_090414_rotating: fraction of samples with equal room and arena positions 0.067
M28_032515_rotating: fraction of samples with equal room and arena positions 0.118
M32_041715_rotating: fraction of samples with equal room and arena positions 0.051
Position series shared by more than one session: []
Identical spike trains across sessions: 0; within a session: 0
[]
Clock check. Spikes and positions are in the same file, each spike filed under a position sample. To show that spike counts and positions share a clock, the spatial information of every cluster is computed with the true alignment and with the spike counts shifted in time against the positions; if the alignment were off, the true alignment would not stand out.
def info_shifted(s, shift_s):
idx, shape = hc16.pixel_index(s, "Rm")
occ = hc16.occupancy(s, idx, shape)
k = int(round(shift_s * s.rate))
out = []
for u in s.cells:
n = s.counts[u]
s.counts[u] = np.roll(n, k)
rm, o = hc16.rate_map(s, u, idx, shape, occ)
s.counts[u] = n
out.append(hc16.information(rm, o))
return np.array(out)
shifts = [0, 0.5, 2, 10, 60, 300]
rows = []
for s in sessions:
if s.env == "rotating_cylinder":
continue
for sh in shifts:
v = info_shifted(s, sh)
rows.append(dict(session=s.id, shift_s=sh, median_info=np.nanmedian(v)))
clk = pd.DataFrame(rows).pivot(index="session", columns="shift_s", values="median_info")
print("Median information content (bits/spike) of each session's clusters, by shift of spikes against positions:")
print(clk.round(3).to_string())
print("Sessions where the true alignment gives the highest median:", int((clk.idxmax(axis=1) == 0).sum()), "of", len(clk))
Median information content (bits/spike) of each session's clusters, by shift of spikes against positions: shift_s 0.0 0.5 2.0 10.0 60.0 300.0 session M10_052214_blackbox 1.402 1.394 1.287 0.954 0.895 0.899 M11_071414_blackbox 1.106 0.874 0.568 0.464 0.474 0.470 M16_090414_stationary1 0.845 0.858 0.691 0.583 0.598 0.561 M16_090414_stationary2 0.894 0.853 0.754 0.663 0.650 0.666 M16_091214_blackbox 2.607 2.395 2.177 2.168 2.188 2.236 M18_100814_blackbox 2.616 2.613 2.607 2.470 2.381 2.477 M18_100914_stationary1 0.673 0.668 0.609 0.547 0.552 0.540 M18_100914_stationary2 0.537 0.515 0.471 0.462 0.450 0.474 M1_040714_blackbox 1.762 1.734 1.523 1.214 1.267 1.224 M28_032515_stationary1 2.747 2.751 2.715 2.692 2.696 2.675 M28_032515_stationary2 1.676 1.633 1.484 1.236 1.283 1.214 M28_060315_blackbox1 1.307 1.304 1.145 0.947 0.994 0.990 M29_061215_blackbox1 1.120 1.084 1.017 0.989 0.968 0.954 M29_112414_stationary1 0.165 0.176 0.153 0.126 0.122 0.124 M29_112414_stationary2 0.302 0.275 0.190 0.178 0.164 0.167 M32_041715_stationary1 1.950 1.929 1.234 0.996 0.999 0.963 M32_041715_stationary2 2.002 1.973 1.496 1.307 1.323 1.423 M32_060315_blackbox1 1.731 1.769 1.480 1.283 1.230 1.268 M33_051715_blackbox 3.172 3.105 2.862 2.720 2.924 2.866 M33_051815_stationary1 1.578 1.556 1.197 1.083 1.083 1.077 M33_051815_stationary2 1.993 1.808 1.934 1.907 2.179 2.357 M34_041015_blackbox 1.109 1.102 1.012 0.934 0.899 0.911 M34_041115_stationary1 0.314 0.294 0.259 0.216 0.206 0.211 M34_041115_stationary2 0.311 0.301 0.231 0.181 0.178 0.173 M35_040115_blackbox 2.158 2.087 1.987 1.772 1.694 1.616 M35_040215_stationary1 0.455 0.422 0.329 0.317 0.340 0.313 M35_040215_stationary2 0.448 0.427 0.282 0.292 0.291 0.281 M36_041415_blackbox 1.176 1.055 0.790 0.683 0.707 0.723 M36_050115_stationary1 0.927 0.917 0.758 0.683 0.679 0.670 M37_050815_blackbox 2.094 1.939 1.838 1.579 1.678 1.700 M37_051115_stationary1 0.454 0.417 0.347 0.319 0.307 0.297 M37_051115_stationary2 0.440 0.381 0.329 0.296 0.293 0.297 M3_041314_blackbox 1.825 1.737 1.516 1.593 1.519 1.800 M9_052614_blackbox 0.738 0.695 0.570 0.460 0.472 0.474 Sessions where the true alignment gives the highest median: 29 of 34
In 29 of 34 stationary sessions the true alignment gives the highest median information content, and the median falls with the shift in most of the rest; this is consistent with spikes and positions sharing a clock, not proof of it.
Cell table and classification¶
One row per cluster and session, with the single-cell measures on unsmoothed 1.25 cm room-frame maps. Classes (STAR Methods, "Classification of Cell Types"; the waveform criterion cannot be applied): interneuron, rate above 2 AP/s; pyramidal, rate below 2 AP/s with an ISI histogram peak below 10 ms; place cell, a pyramidal cell with rate 0.1-2 AP/s, coherence above 0.4 and information above 0.4 bits/spike ("Characterization of Place Cells"). Clusters below 2 AP/s without bursts are left unclassified. A variant drops the burst criterion (rate alone).
"Standard recordings" (the session a cell is classified in) is open in the paper; the claims
file names two readings. R1: each cell once, from its first stationary-environment session (the
large box, or the first stationary arena session of a day). R2: every stationary session (large
box, stationary arena 1 and 2) on its own, a cell counted per session. Sessions of a day share a
cluster cutting file (the cl... part of the name), so a cluster name within a cut is taken to be
the same cell across that day's sessions (an assumption: the files carry no other identifier).
cells = hc16.cell_table(sessions)
cells["cls"] = hc16.classify(cells)
cells["cls_rate"] = hc16.classify(cells, use_burst=False)
stationary = cells[cells.env != "rotating_cylinder"].copy()
first_sid = {}
for s in sessions:
if s.env == "rotating_cylinder":
continue
first_sid.setdefault(f"{s.mouse}_{s.date}", s.id) # sessions are sorted: stationary1 before stationary2
R1 = stationary[stationary.session.isin(set(first_sid.values()))].copy()
R2 = stationary.copy()
print("R1 sessions:", sorted(first_sid.values()))
for name, t in [("R1", R1), ("R2", R2)]:
print(f"\n{name}: classes by genotype (rate and bursting)")
print(t.groupby(["genotype", "cls"]).size().unstack(fill_value=0))
print(f"{name}: classes by genotype (rate only)")
print(t.groupby(["genotype", "cls_rate"]).size().unstack(fill_value=0))
print("\nRanges of the measures used (all stationary sessions):")
print(R2[["rate", "coherence", "info", "active", "burst_prob", "isi_mean", "isi_peak_ms", "infield_mean", "infield_20pk"]]
.describe().round(3).to_string())
R1 sessions: ['M10_052214_blackbox', 'M11_071414_blackbox', 'M16_090414_stationary1', 'M16_091214_blackbox', 'M18_100814_blackbox', 'M18_100914_stationary1', 'M1_040714_blackbox', 'M28_032515_stationary1', 'M28_060315_blackbox1', 'M29_061215_blackbox1', 'M29_112414_stationary1', 'M32_041715_stationary1', 'M32_060315_blackbox1', 'M33_051715_blackbox', 'M33_051815_stationary1', 'M34_041015_blackbox', 'M34_041115_stationary1', 'M35_040115_blackbox', 'M35_040215_stationary1', 'M36_041415_blackbox', 'M36_050115_stationary1', 'M37_050815_blackbox', 'M37_051115_stationary1', 'M3_041314_blackbox', 'M9_052614_blackbox']
R1: classes by genotype (rate and bursting)
cls interneuron nonspatial place unclassified
genotype
Fmr1-null 106 48 51 33
WT 68 60 48 27
R1: classes by genotype (rate only)
cls_rate interneuron nonspatial place
genotype
Fmr1-null 106 78 54
WT 68 84 51
R2: classes by genotype (rate and bursting)
cls interneuron nonspatial place unclassified
genotype
Fmr1-null 166 62 70 44
WT 103 91 55 42
R2: classes by genotype (rate only)
cls_rate interneuron nonspatial place
genotype
Fmr1-null 166 103 73
WT 103 129 59
Ranges of the measures used (all stationary sessions):
rate coherence info active burst_prob isi_mean isi_peak_ms infield_mean infield_20pk
count 633.000 633.000 633.000 633.000 632.000 632.000 619.000 558.000 280.000
mean 4.622 0.377 1.560 0.528 0.142 10.103 13.006 8.113 14.850
std 7.771 0.283 1.645 0.346 0.112 37.935 28.797 9.591 9.660
min 0.001 -0.067 0.011 0.001 0.000 0.020 1.500 0.254 1.099
25% 0.332 0.154 0.256 0.204 0.045 0.215 4.500 2.129 8.306
50% 1.382 0.338 1.024 0.536 0.131 0.709 5.500 4.601 13.037
75% 4.581 0.536 2.250 0.890 0.215 2.971 8.500 9.503 19.166
max 50.305 1.335 8.968 0.997 0.519 509.042 393.500 56.872 53.765
Two variants of the population and of the maps, shown with the claims they bear on. The data set gives no isolation measure, so the fraction of interspike intervals below 2 ms (refractory violations) is shown as a proxy: a cluster mixing several units has many. The refractory filter keeps clusters with at most 0.5% of intervals below 2 ms, a standard choice. The spatial scale is an assumption, so the single-cell measures are also computed on the code's 32 x 32 grid of the 256 x 256 tracker frame (8 tracker pixels a side), which needs no scale.
print("Fraction of ISIs below 2 ms by class, all sessions:")
print(cells.groupby("cls").refractory.describe(percentiles=[0.5, 0.9]).round(4).to_string())
ISO = 0.005
print(f"Clusters with more than {ISO:.1%} of ISIs below 2 ms:", int((cells.refractory > ISO).sum()), "of", len(cells))
cells_code = hc16.cell_table(sessions, index_fn=hc16.pixel_index_code)
cells_code["cls"] = hc16.classify(cells_code)
print("32 x 32 grid pixel side in cm at the assumed scale:",
{env: f"{8 / g.px_per_cm.max():.2f} to {8 / g.px_per_cm.min():.2f}" for env, g in sess.groupby("env")})
print("\nClass on 1.25 cm pixels (rows) against the 32 x 32 grid (columns), all sessions:")
print(pd.crosstab(cells.cls, cells_code.cls).to_string())
R1_code = cells_code.loc[R1.index]; R2_code = cells_code.loc[R2.index]
R1_iso = R1[R1.refractory <= ISO]; R2_iso = R2[R2.refractory <= ISO]
for name, t in [("R1, 32 x 32 grid", R1_code), ("R2, 32 x 32 grid", R2_code),
("R1, refractory filter", R1_iso), ("R2, refractory filter", R2_iso)]:
print(f"\n{name}: classes by genotype")
print(t.groupby(["genotype", "cls"]).size().unstack(fill_value=0).to_string())
print("\nInterneuron rate (AP/s), R1: quantiles by genotype")
print(R1[R1.cls == "interneuron"].groupby("genotype").rate.describe(percentiles=[0.25, 0.5, 0.75]).round(2).to_string())
Fraction of ISIs below 2 ms by class, all sessions:
count mean std min 50% 90% max
cls
interneuron 328.0 0.0028 0.0049 0.0 0.0008 0.0071 0.0339
nonspatial 189.0 0.0013 0.0066 0.0 0.0000 0.0024 0.0769
place 141.0 0.0006 0.0014 0.0 0.0000 0.0018 0.0104
unclassified 116.0 0.0005 0.0023 0.0 0.0000 0.0007 0.0211
Clusters with more than 0.5% of ISIs below 2 ms: 70 of 774
32 x 32 grid pixel side in cm at the assumed scale: {'big_box': '1.82 to 2.22', 'rotating_cylinder': '1.36 to 1.44', 'stationary_cylinder': '1.36 to 1.46'}
Class on 1.25 cm pixels (rows) against the 32 x 32 grid (columns), all sessions:
cls interneuron nonspatial place unclassified
cls
interneuron 328 0 0 0
nonspatial 0 156 33 0
place 0 12 129 0
unclassified 0 0 0 116
R1, 32 x 32 grid: classes by genotype
cls interneuron nonspatial place unclassified
genotype
Fmr1-null 106 36 63 33
WT 68 53 55 27
R2, 32 x 32 grid: classes by genotype
cls interneuron nonspatial place unclassified
genotype
Fmr1-null 166 51 81 44
WT 103 81 65 42
R1, refractory filter: classes by genotype
cls interneuron nonspatial place unclassified
genotype
Fmr1-null 91 47 51 33
WT 64 57 47 26
R2, refractory filter: classes by genotype
cls interneuron nonspatial place unclassified
genotype
Fmr1-null 134 59 70 44
WT 95 86 54 41
Interneuron rate (AP/s), R1: quantiles by genotype
count mean std min 25% 50% 75% max
genotype
Fmr1-null 106.0 11.47 10.67 2.08 3.54 6.95 16.60 50.30
WT 68.0 7.27 7.04 2.03 2.83 4.34 8.47 34.48
unit-and-cell-counts¶
Of 1,115 clusters, 499 came from 12 WT and 616 from 9 Fmr1-null mice; 226 WT and 334 Fmr1-null well-isolated cells formed the data set.
Criterion: mice counted exactly (11 or 12 WT); clusters and cells counted once or per session. The data set holds no isolation measures, so the well-isolated cells cannot be counted; the cluster counts are given with and without cluster 0.
mice = sess.groupby("genotype").mouse.unique()
print("Mice in the data set:", {g: sorted(v) for g, v in mice.items()})
print("Mice per genotype:", sess.groupby("genotype").mouse.nunique().to_dict(), "(paper: WT 12, Fmr1-null 9)")
cnt = []
for s in sessions:
cnt.append(dict(session=s.id, genotype=s.genotype, env=s.env, mouse=s.mouse, day=f"{s.mouse}_{s.date}",
cut=s.cut, clusters=len(s.cells), with_c0=len(s.counts)))
cnt = pd.DataFrame(cnt)
# once: each day's cut counted once (a cluster name within a cut is one cell across the day's sessions)
once = cnt.drop_duplicates(["day", "cut"])
once_day = cnt.sort_values("session").drop_duplicates(["day"])
tab = pd.DataFrame({
"once per cut file": once.groupby("genotype").clusters.sum(),
"once per day (R1)": R1.groupby("genotype").size(),
"per session, stationary (R2)": R2.groupby("genotype").size(),
"per session, all sessions": cnt.groupby("genotype").clusters.sum(),
"once per cut, with cluster 0": once.groupby("genotype").with_c0.sum(),
"per session, all, with cluster 0": cnt.groupby("genotype").with_c0.sum(),
})
print("\nClusters (cluster 0 left out unless said):")
print(tab.T.to_string())
print("Paper: clusters WT 499, Fmr1-null 616; well-isolated cells WT 226, Fmr1-null 334 (tolerance 10%)")
for col in tab.columns:
w, k = tab.loc["WT", col], tab.loc["Fmr1-null", col]
print(f" {col}: WT {w} ({'in' if abs(w - 499) <= 49.9 else 'out of'} 499 +/- 49.9), "
f"Fmr1-null {k} ({'in' if abs(k - 616) <= 61.6 else 'out of'} 616 +/- 61.6); "
f"as cells: WT {'in' if abs(w - 226) <= 22.6 else 'out of'} 226 +/- 22.6, "
f"Fmr1-null {'in' if abs(k - 334) <= 33.4 else 'out of'} 334 +/- 33.4")
Mice in the data set: {'Fmr1-null': ['M10', 'M18', 'M29', 'M33', 'M34', 'M37', 'M9'], 'WT': ['M1', 'M11', 'M16', 'M28', 'M3', 'M32', 'M35', 'M36']}
Mice per genotype: {'Fmr1-null': 7, 'WT': 8} (paper: WT 12, Fmr1-null 9)
Clusters (cluster 0 left out unless said):
genotype Fmr1-null WT
once per cut file 294 203
once per day (R1) 238 203
per session, stationary (R2) 342 291
per session, all sessions 408 366
once per cut, with cluster 0 389 276
per session, all, with cluster 0 557 490
Paper: clusters WT 499, Fmr1-null 616; well-isolated cells WT 226, Fmr1-null 334 (tolerance 10%)
once per cut file: WT 203 (out of 499 +/- 49.9), Fmr1-null 294 (out of 616 +/- 61.6); as cells: WT out of 226 +/- 22.6, Fmr1-null out of 334 +/- 33.4
once per day (R1): WT 203 (out of 499 +/- 49.9), Fmr1-null 238 (out of 616 +/- 61.6); as cells: WT out of 226 +/- 22.6, Fmr1-null out of 334 +/- 33.4
per session, stationary (R2): WT 291 (out of 499 +/- 49.9), Fmr1-null 342 (out of 616 +/- 61.6); as cells: WT out of 226 +/- 22.6, Fmr1-null in 334 +/- 33.4
per session, all sessions: WT 366 (out of 499 +/- 49.9), Fmr1-null 408 (out of 616 +/- 61.6); as cells: WT out of 226 +/- 22.6, Fmr1-null out of 334 +/- 33.4
once per cut, with cluster 0: WT 276 (out of 499 +/- 49.9), Fmr1-null 389 (out of 616 +/- 61.6); as cells: WT out of 226 +/- 22.6, Fmr1-null out of 334 +/- 33.4
per session, all, with cluster 0: WT 490 (in 499 +/- 49.9), Fmr1-null 557 (in 616 +/- 61.6); as cells: WT out of 226 +/- 22.6, Fmr1-null out of 334 +/- 33.4
Result: blocked (data missing). The data set holds 8 WT and 7 Fmr1-null mice, where the paper has 12 (or 11) and 9: it is a subset of the paper's data, so its counts cannot test the paper's. The well-isolated cells cannot be selected either, as the data set has no isolation measures. For the record, the cluster counts are within 10% of 499 and 616 only when every session is counted on its own and cluster 0 (the unsorted spikes of each tetrode) is included, 490 WT and 557 Fmr1-null, which with 15 of 21 mice is a coincidence; counted once per cut file without cluster 0 there are 203 WT and 294 Fmr1-null clusters.
figs4-remapping-similar (blocked)¶
The claim compares the same cells in a square box and the circular arena. The table pairs each mouse's box sessions with its arena sessions, with their dates and cut files.
rows = []
for m, g in sess.groupby("mouse"):
for _, b in g[g.env == "big_box"].iterrows():
for _, a in g[g.env != "big_box"].iterrows():
rows.append(dict(mouse=m, genotype=b.genotype, box=b.session, box_cut=b.cut, arena=a.session,
arena_cut=a.cut, same_day=b.date == a.date, same_cut=b.cut == a.cut))
fx = pd.DataFrame(rows)
print(fx.to_string())
print("Box and arena session pairs:", len(fx), "; on the same day:", int(fx.same_day.sum()),
"; with the same cut file:", int(fx.same_cut.sum()))
print("Small box sessions in the data set:", int((files.env == "small_box").sum()))
mouse genotype box box_cut arena arena_cut same_day same_cut 0 M16 WT M16_091214_blackbox cl123115 M16_090414_rotating cl101015 False False 1 M16 WT M16_091214_blackbox cl123115 M16_090414_stationary1 cl101015 False False 2 M16 WT M16_091214_blackbox cl123115 M16_090414_stationary2 cl101015 False False 3 M18 Fmr1-null M18_100814_blackbox cl120915 M18_100914_rotating cl120815 False False 4 M18 Fmr1-null M18_100814_blackbox cl120915 M18_100914_stationary1 cl120815 False False 5 M18 Fmr1-null M18_100814_blackbox cl120915 M18_100914_stationary2 cl120815 False False 6 M28 WT M28_060315_blackbox1 cl123115 M28_032515_rotating cl020216 False False 7 M28 WT M28_060315_blackbox1 cl123115 M28_032515_stationary1 cl020216 False False 8 M28 WT M28_060315_blackbox1 cl123115 M28_032515_stationary2 cl020216 False False 9 M29 Fmr1-null M29_061215_blackbox1 cl121115 M29_112414_rotating cl020316 False False 10 M29 Fmr1-null M29_061215_blackbox1 cl121115 M29_112414_stationary1 cl120715 False False 11 M29 Fmr1-null M29_061215_blackbox1 cl121115 M29_112414_stationary2 cl020316 False False 12 M32 WT M32_060315_blackbox1 cl123115 M32_041715_rotating cl101015 False False 13 M32 WT M32_060315_blackbox1 cl123115 M32_041715_stationary1 cl101015 False False 14 M32 WT M32_060315_blackbox1 cl123115 M32_041715_stationary2 cl101015 False False 15 M33 Fmr1-null M33_051715_blackbox cl121515 M33_051815_rotating cl112715 False False 16 M33 Fmr1-null M33_051715_blackbox cl121515 M33_051815_stationary1 cl112715 False False 17 M33 Fmr1-null M33_051715_blackbox cl121515 M33_051815_stationary2 cl112715 False False 18 M34 Fmr1-null M34_041015_blackbox cl121615 M34_041115_stationary1 cl101115 False False 19 M34 Fmr1-null M34_041015_blackbox cl121615 M34_041115_stationary2 cl101115b False False 20 M35 WT M35_040115_blackbox cl123115 M35_040215_stationary1 cl100915 False False 21 M35 WT M35_040115_blackbox cl123115 M35_040215_stationary2 cl100915 False False 22 M36 WT M36_041415_blackbox cl123115 M36_050115_stationary1 cl120215 False False 23 M37 Fmr1-null M37_050815_blackbox cl121615 M37_051115_rotating cl101015 False False 24 M37 Fmr1-null M37_050815_blackbox cl121615 M37_051115_stationary1 cl101015 False False 25 M37 Fmr1-null M37_050815_blackbox cl121615 M37_051115_stationary2 cl101015 False False Box and arena session pairs: 26 ; on the same day: 0 ; with the same cut file: 0 Small box sessions in the data set: 0
No box session shares a day or a cut file with an arena session of the same mouse, and the data set has no waveforms, so no cell can be followed from the square to the circular enclosure. The claim is blocked (data missing): the small box sessions recorded on the days of arena sessions (Table S2, for example M32 on 04-17-15) are not in the data set.
interneuron-proportion¶
A lower proportion of the well-isolated cells is classified as interneurons in WT (28, 12.4%) than in Fmr1-null mice (98, 29.3%).
Criterion: percentages within tolerance (12.4 +/- 1.24, 29.3 +/- 2.93) and a two-proportion z test with p < 0.05, WT lower. Classification by rate and bursting, the variant the claims file names when waveforms are missing; rate alone is shown too. Readings R1 and R2 for the session.
def proportions(t, col, cls, denom_classes=None):
out = {}
for g in ["WT", "Fmr1-null"]:
tt = t[t.genotype == g]
d = tt if denom_classes is None else tt[tt[col].isin(denom_classes)]
out[g] = (int((tt[col] == cls).sum()), len(d))
z, p = hc16.prop_test(out["WT"][0], out["WT"][1], out["Fmr1-null"][0], out["Fmr1-null"][1])
return out, z, p
rows = []
for rname, t in [("R1", R1), ("R2", R2), ("R1 refractory filter", R1_iso), ("R2 refractory filter", R2_iso)]:
for col in ["cls", "cls_rate"]:
for den_name, den in [("all clusters", None), ("classified cells", ["interneuron", "place", "nonspatial"])]:
if col == "cls_rate" and den is not None:
continue
o, zz, pp = proportions(t, col, "interneuron", den)
rows.append(dict(reading=rname, classes="rate+burst" if col == "cls" else "rate only",
denominator=den_name, WT=f"{o['WT'][0]}/{o['WT'][1]}",
WT_pct=100 * o["WT"][0] / o["WT"][1], KO=f"{o['Fmr1-null'][0]}/{o['Fmr1-null'][1]}",
KO_pct=100 * o["Fmr1-null"][0] / o["Fmr1-null"][1], z=zz, p_two_sided=pp))
ip = pd.DataFrame(rows)
ip["WT_in_tol"] = (ip.WT_pct - 12.4).abs() <= 1.24
ip["KO_in_tol"] = (ip.KO_pct - 29.3).abs() <= 2.93
print(ip.round(4).to_string())
print("\nBy mouse (R1, rate+burst, all clusters): interneuron fraction")
print(R1.groupby(["genotype", "mouse"]).apply(lambda d: pd.Series(dict(n=len(d), interneurons=(d.cls == "interneuron").sum(),
frac=(d.cls == "interneuron").mean()))).round(3).to_string())
reading classes denominator WT WT_pct KO KO_pct z p_two_sided WT_in_tol KO_in_tol
0 R1 rate+burst all clusters 68/203 33.4975 106/238 44.5378 -2.3643 0.0181 False False
1 R1 rate+burst classified cells 68/176 38.6364 106/205 51.7073 -2.5535 0.0107 False False
2 R1 rate only all clusters 68/203 33.4975 106/238 44.5378 -2.3643 0.0181 False False
3 R2 rate+burst all clusters 103/291 35.3952 166/342 48.5380 -3.3337 0.0009 False False
4 R2 rate+burst classified cells 103/249 41.3655 166/298 55.7047 -3.3406 0.0008 False False
5 R2 rate only all clusters 103/291 35.3952 166/342 48.5380 -3.3337 0.0009 False False
6 R1 refractory filter rate+burst all clusters 64/194 32.9897 91/222 40.9910 -1.6838 0.0922 False False
7 R1 refractory filter rate+burst classified cells 64/168 38.0952 91/189 48.1481 -1.9128 0.0558 False False
8 R1 refractory filter rate only all clusters 64/194 32.9897 91/222 40.9910 -1.6838 0.0922 False False
9 R2 refractory filter rate+burst all clusters 95/276 34.4203 134/307 43.6482 -2.2779 0.0227 False False
10 R2 refractory filter rate+burst classified cells 95/235 40.4255 134/263 50.9506 -2.3526 0.0186 False False
11 R2 refractory filter rate only all clusters 95/276 34.4203 134/307 43.6482 -2.2779 0.0227 False False
By mouse (R1, rate+burst, all clusters): interneuron fraction
n interneurons frac
genotype mouse
Fmr1-null M10 5.0 2.0 0.400
M18 40.0 13.0 0.325
M29 41.0 22.0 0.537
M33 20.0 4.0 0.200
M34 72.0 40.0 0.556
M37 48.0 18.0 0.375
M9 12.0 7.0 0.583
WT M1 9.0 4.0 0.444
M11 8.0 5.0 0.625
M16 77.0 24.0 0.312
M28 24.0 7.0 0.292
M3 3.0 1.0 0.333
M32 39.0 12.0 0.308
M35 28.0 10.0 0.357
M36 15.0 5.0 0.333
Result: partially reproduced. Fmr1-null has the larger share of interneurons, as claimed, under every reading: R1, all clusters, 44.5% against 33.5% (z = -2.36, p = 0.018); R2, 48.5% against 35.4% (z = -3.33, p = 0.0009); with the refractory filter p = 0.092 (R1) and 0.023 (R2). The percentages are far from the paper's under every reading: WT 33.0% to 41.4% against 12.4 +/- 1.24%, Fmr1-null 41.0% to 55.7% against 29.3 +/- 2.93%. Interneurons here are clusters above 2 AP/s, without the waveform criterion, among every sorted cluster. Few clusters have refractory violations (70 of 774 have more than 0.5% of intervals below 2 ms), and the filter changes the percentages by at most 4.9 points, so this filter does not remove the excess. It is a weak proxy for the paper's isolation measures: interneuron rates are still 4.34 (WT) and 6.95 (Fmr1-null) AP/s at the median in R1, so whether poorly isolated clusters explain the excess cannot be told from these data.
place-cell-proportion¶
Place cells are less prevalent in Fmr1-null mice (141, 42.2%) than in WT mice (108, 47.8%).
Criterion: counts and percentages within tolerance under one denominator (all cells, or the principal cells), and a two-proportion z test with p < 0.05 (one-sided; two-sided as a variant), Fmr1-null lower.
rows = []
for rname, t in [("R1", R1), ("R2", R2), ("R1 32x32", R1_code), ("R2 32x32", R2_code),
("R1 refractory filter", R1_iso), ("R2 refractory filter", R2_iso)]:
for col in [c for c in ["cls", "cls_rate"] if c in t]:
for den_name, den in [("all clusters", None), ("classified cells", ["interneuron", "place", "nonspatial"]),
("principal cells", ["place", "nonspatial"])]:
if col == "cls_rate" and den == ["interneuron", "place", "nonspatial"]:
continue
o, zz, pp = proportions(t, col, "place", den)
rows.append(dict(reading=rname, classes="rate+burst" if col == "cls" else "rate only",
denominator=den_name, WT=o["WT"][0], WT_n=o["WT"][1], WT_pct=100 * o["WT"][0] / o["WT"][1],
KO=o["Fmr1-null"][0], KO_n=o["Fmr1-null"][1],
KO_pct=100 * o["Fmr1-null"][0] / o["Fmr1-null"][1], z=zz,
p_one_sided=stats.norm.sf(zz), p_two_sided=pp))
pc = pd.DataFrame(rows)
pc["counts_in_tol"] = ((pc.WT - 108).abs() <= 10.8) & ((pc.KO - 141).abs() <= 14.1)
pc["pct_in_tol"] = ((pc.WT_pct - 47.8).abs() <= 4.78) & ((pc.KO_pct - 42.2).abs() <= 4.22)
print(pc.round(4).to_string())
print("\nBy mouse (R1, rate+burst): place cells / all clusters")
print(R1.groupby(["genotype", "mouse"]).apply(lambda d: f"{(d.cls == 'place').sum()}/{len(d)}").to_string())
reading classes denominator WT WT_n WT_pct KO KO_n KO_pct z p_one_sided p_two_sided counts_in_tol pct_in_tol
0 R1 rate+burst all clusters 48 203 23.6453 51 238 21.4286 0.5561 0.2891 0.5782 False False
1 R1 rate+burst classified cells 48 176 27.2727 51 205 24.8780 0.5314 0.2976 0.5952 False False
2 R1 rate+burst principal cells 48 108 44.4444 51 99 51.5152 -1.0173 0.8455 0.3090 False False
3 R1 rate only all clusters 51 203 25.1232 54 238 22.6891 0.5982 0.2749 0.5497 False False
4 R1 rate only principal cells 51 135 37.7778 54 132 40.9091 -0.5237 0.6998 0.6005 False False
5 R2 rate+burst all clusters 55 291 18.9003 70 342 20.4678 -0.4937 0.6892 0.6215 False False
6 R2 rate+burst classified cells 55 249 22.0884 70 298 23.4899 -0.3888 0.6513 0.6974 False False
7 R2 rate+burst principal cells 55 146 37.6712 70 132 53.0303 -2.5707 0.9949 0.0101 False False
8 R2 rate only all clusters 59 291 20.2749 73 342 21.3450 -0.3303 0.6294 0.7412 False False
9 R2 rate only principal cells 59 188 31.3830 73 176 41.4773 -2.0018 0.9773 0.0453 False False
10 R1 32x32 rate+burst all clusters 55 203 27.0936 63 238 26.4706 0.1473 0.4414 0.8829 False False
11 R1 32x32 rate+burst classified cells 55 176 31.2500 63 205 30.7317 0.1091 0.4566 0.9131 False False
12 R1 32x32 rate+burst principal cells 55 108 50.9259 63 99 63.6364 -1.8452 0.9675 0.0650 False False
13 R2 32x32 rate+burst all clusters 65 291 22.3368 81 342 23.6842 -0.4011 0.6558 0.6884 False False
14 R2 32x32 rate+burst classified cells 65 249 26.1044 81 298 27.1812 -0.2835 0.6116 0.7768 False False
15 R2 32x32 rate+burst principal cells 65 146 44.5205 81 132 61.3636 -2.8083 0.9975 0.0050 False False
16 R1 refractory filter rate+burst all clusters 47 194 24.2268 51 222 22.9730 0.3006 0.3818 0.7637 False False
17 R1 refractory filter rate+burst classified cells 47 168 27.9762 51 189 26.9841 0.2097 0.4170 0.8339 False False
18 R1 refractory filter rate+burst principal cells 47 104 45.1923 51 98 52.0408 -0.9734 0.8348 0.3304 False False
19 R1 refractory filter rate only all clusters 50 194 25.7732 54 222 24.3243 0.3405 0.3668 0.7335 False False
20 R1 refractory filter rate only principal cells 50 130 38.4615 54 131 41.2214 -0.4553 0.6756 0.6489 False False
21 R2 refractory filter rate+burst all clusters 54 276 19.5652 70 307 22.8013 -0.9534 0.8298 0.3404 False False
22 R2 refractory filter rate+burst classified cells 54 235 22.9787 70 263 26.6160 -0.9370 0.8256 0.3487 False False
23 R2 refractory filter rate+burst principal cells 54 140 38.5714 70 129 54.2636 -2.5794 0.9951 0.0099 False False
24 R2 refractory filter rate only all clusters 58 276 21.0145 73 307 23.7785 -0.7983 0.7877 0.4247 False False
25 R2 refractory filter rate only principal cells 58 181 32.0442 73 173 42.1965 -1.9776 0.9760 0.0480 False False
By mouse (R1, rate+burst): place cells / all clusters
genotype mouse
Fmr1-null M10 0/5
M18 8/40
M29 8/41
M33 3/20
M34 16/72
M37 14/48
M9 2/12
WT M1 4/9
M11 1/8
M16 14/77
M28 6/24
M3 1/3
M32 13/39
M35 7/28
M36 2/15
Result: not reproduced. No reading gives the paper's counts or percentages: with all clusters as the denominator there are 48 WT (23.6%) and 51 Fmr1-null (21.4%) place cells in R1, and 55 (18.9%) and 70 (20.5%) in R2, against 108 (47.8%) and 141 (42.2%); with principal cells as the denominator WT is 44.4% and Fmr1-null 51.5% (R1). No one-sided test over the 26 readings, including the 32 x 32 grid and the refractory filter, finds Fmr1-null lower (smallest one-sided p 0.27); with principal cells in R2 Fmr1-null is higher (53.0% against 37.7%, two-sided p = 0.010; on the 32 x 32 grid 61.4% against 44.5%, p = 0.005).
figs3-nonspatial-properties-similar¶
Waveform width, burst probability, interspike interval and overall firing rate do not differ between genotypes.
Criterion: a two-way genotype x cell class ANOVA per measure (Figure S3 legend); burst probability is the fraction of ISIs below 10 ms; ISI as the mean per cell (median as a variant); reproduced when no genotype effect has p < 0.05. Waveforms are not in the data set, so width is not tested. Cells: the classified clusters (place, non-spatial pyramidal, interneuron) of R1; R2 shown as a variant.
def anova_rows(t, measures, label):
rows = []
for m in measures:
a = hc16.anova2(t, m)
rows.append(dict(reading=label, measure=m, n=int(t[m].notna().sum()),
F_genotype=a.loc["C(genotype)", "F"], df_resid=int(a.loc["Residual", "df"]),
p_genotype=a.loc["C(genotype)", "PR(>F)"], eta_p2_genotype=a.loc["C(genotype)", "eta_p2"],
p_class=a.loc["C(cls)", "PR(>F)"], p_interaction=a.loc["C(genotype):C(cls)", "PR(>F)"]))
return rows
rows = []
for label, t in [("R1", R1), ("R2", R2), ("R1 refractory filter", R1_iso), ("R2 refractory filter", R2_iso)]:
tc = t[t.cls.isin(["place", "nonspatial", "interneuron"])].copy()
rows += anova_rows(tc, ["burst_prob", "isi_mean", "isi_median", "rate"], label)
f3 = pd.DataFrame(rows)
print(f3.round(4).to_string())
tc = R1[R1.cls.isin(["place", "nonspatial", "interneuron"])]
print("\nR1 means by genotype and class:")
print(tc.groupby(["cls", "genotype"])[["burst_prob", "isi_mean", "isi_median", "rate"]].agg(["mean", "count"]).round(3).to_string())
print("\nR1 by mouse: mean burst probability and rate of classified clusters")
print(tc.groupby(["genotype", "mouse"])[["burst_prob", "rate"]].mean().round(3).to_string())
reading measure n F_genotype df_resid p_genotype eta_p2_genotype p_class p_interaction
0 R1 burst_prob 381 2.3626 375 0.1251 0.0063 0.0 0.0775
1 R1 isi_mean 381 0.7112 375 0.3996 0.0019 0.0 0.4041
2 R1 isi_median 381 0.2353 375 0.6279 0.0006 0.0 0.7448
3 R1 rate 381 8.2848 375 0.0042 0.0216 0.0 0.0086
4 R2 burst_prob 547 4.8146 541 0.0286 0.0088 0.0 0.0989
5 R2 isi_mean 547 0.7055 541 0.4013 0.0013 0.0 0.4043
6 R2 isi_median 547 0.3706 541 0.5429 0.0007 0.0 0.5839
7 R2 rate 547 12.1301 541 0.0005 0.0219 0.0 0.0024
8 R1 refractory filter burst_prob 357 3.1192 351 0.0782 0.0088 0.0 0.0112
9 R1 refractory filter isi_mean 357 0.7669 351 0.3818 0.0022 0.0 0.3834
10 R1 refractory filter isi_median 357 0.2954 351 0.5871 0.0008 0.0 0.6958
11 R1 refractory filter rate 357 3.3099 351 0.0697 0.0093 0.0 0.1460
12 R2 refractory filter burst_prob 498 8.5237 492 0.0037 0.0170 0.0 0.0061
13 R2 refractory filter isi_mean 498 0.4894 492 0.4845 0.0010 0.0 0.5367
14 R2 refractory filter isi_median 498 0.3585 492 0.5496 0.0007 0.0 0.5991
15 R2 refractory filter rate 498 3.6393 492 0.0570 0.0073 0.0 0.1564
R1 means by genotype and class:
burst_prob isi_mean isi_median rate
mean count mean count mean count mean count
cls genotype
interneuron Fmr1-null 0.148 106 0.179 106 0.075 106 11.473 106
WT 0.185 68 0.239 68 0.072 68 7.270 68
nonspatial Fmr1-null 0.153 48 14.297 48 4.908 48 0.531 48
WT 0.131 60 20.313 60 6.373 60 0.373 60
place Fmr1-null 0.200 51 1.847 51 0.161 51 0.915 51
WT 0.224 48 1.681 48 0.160 48 0.956 48
R1 by mouse: mean burst probability and rate of classified clusters
burst_prob rate
genotype mouse
Fmr1-null M10 0.242 1.735
M18 0.198 3.812
M29 0.099 7.586
M33 0.149 2.320
M34 0.152 7.140
M37 0.188 7.741
M9 0.185 5.104
WT M1 0.146 1.940
M11 0.222 2.759
M16 0.166 3.553
M28 0.132 3.240
M3 0.112 2.278
M32 0.245 1.941
M35 0.170 4.203
M36 0.136 4.224
Result: partially reproduced. In R1 (381 classified clusters) burst probability and the ISI show no genotype effect (burst p = 0.13; mean ISI p = 0.40; median ISI p = 0.63), but the rate does (F = 8.28, p = 0.0042, partial eta squared 0.022, with an interaction p = 0.0086; Fmr1-null interneurons fire at 11.5 against 7.3 AP/s). In R2 burst probability also differs (p = 0.029) and the rate (p = 0.0005). With the refractory filter the rate effect falls below significance (R1 p = 0.070, R2 p = 0.057), so that in R1 no measure differs, but in R2 burst probability does (p = 0.0037). Waveform width is not in the data set and is not tested.
fig2-spatial-quality-similar¶
In a fixed environment, spatial firing quality (coherence, proportion of active pixels, information content, number of place fields, in-field rate) differs between place cells and interneurons but not between genotypes.
Criterion: a two-way genotype x cell class ANOVA per measure on cells recorded in the box; the box is open (large, small or both), and only the large box is in the data set. Fields are 4-connected pixels above (a) the cell's mean rate or (b) 20% of its peak rate, at least 9 pixels. Reproduced when no genotype effect has p < 0.05 and the class effect has p < 0.05 for coherence, active pixels, information and in-field rate.
box = cells[(cells.env == "big_box") & cells.cls.isin(["place", "nonspatial", "interneuron"])].copy()
print("Large box cells by genotype and class:")
print(box.groupby(["genotype", "cls"]).size().unstack())
print("Mice:", box.groupby("genotype").mouse.nunique().to_dict())
measures = ["coherence", "active", "info", "nfields_mean", "infield_mean", "nfields_20pk", "infield_20pk"]
f2 = pd.DataFrame(anova_rows(box, measures, "large box"))
box_code = cells_code[(cells_code.env == "big_box") & cells_code.cls.isin(["place", "nonspatial", "interneuron"])].copy()
box_iso = box[box.refractory <= ISO]
f2 = pd.concat([f2, pd.DataFrame(anova_rows(box_code, measures[:-1], "32x32 grid")),
pd.DataFrame(anova_rows(box_iso, measures, "refractory filter"))], ignore_index=True)
print(f2.round(4).to_string())
print("32x32 grid, large box cells by genotype and class:")
print(box_code.groupby(["genotype", "cls"]).size().unstack().to_string())
print("\nMeans by class and genotype:")
print(box.groupby(["cls", "genotype"])[measures].mean().round(3).to_string())
print("\nRanges:", box[measures].describe().loc[["min", "max"]].round(3).to_string())
fig, axs = plt.subplots(1, 5, figsize=(15, 3))
for ax, m in zip(axs, ["coherence", "active", "info", "nfields_mean", "infield_mean"]):
for i, (c, g) in enumerate([(c, g) for c in ["place", "nonspatial", "interneuron"] for g in ["WT", "Fmr1-null"]]):
v = box[(box.cls == c) & (box.genotype == g)][m].dropna()
ax.errorbar(i, v.mean(), v.sem(), fmt="o", color="k" if g == "WT" else "r")
ax.set_xticks(range(6)); ax.set_xticklabels(["PC WT", "PC KO", "NS WT", "NS KO", "IN WT", "IN KO"], rotation=60)
ax.set_title(m)
plt.tight_layout(); plt.show()
Large box cells by genotype and class:
cls interneuron nonspatial place
genotype
Fmr1-null 46 35 36
WT 33 29 34
Mice: {'Fmr1-null': 7, 'WT': 8}
reading measure n F_genotype df_resid p_genotype eta_p2_genotype p_class p_interaction
0 large box coherence 213 9.0099 207 0.0030 0.0417 0.0000 0.0077
1 large box active 213 18.7084 207 0.0000 0.0829 0.0000 0.0098
2 large box info 213 7.2717 207 0.0076 0.0339 0.0000 0.1789
3 large box nfields_mean 213 6.6162 207 0.0108 0.0310 0.0000 0.0951
4 large box infield_mean 199 2.5431 193 0.1124 0.0130 0.0000 0.0541
5 large box nfields_20pk 213 0.4994 207 0.4805 0.0024 0.0000 0.6933
6 large box infield_20pk 100 0.6435 94 0.4245 0.0068 0.0004 0.5706
7 32x32 grid coherence 213 13.7992 207 0.0003 0.0625 0.0000 0.0036
8 32x32 grid active 213 10.2814 207 0.0016 0.0473 0.0000 0.0998
9 32x32 grid info 213 7.3377 207 0.0073 0.0342 0.0000 0.0294
10 32x32 grid nfields_mean 213 3.4395 207 0.0651 0.0163 0.0000 0.1139
11 32x32 grid infield_mean 200 2.5852 194 0.1095 0.0132 0.0000 0.0558
12 32x32 grid nfields_20pk 213 0.0778 207 0.7805 0.0004 0.0000 0.8336
13 refractory filter coherence 205 7.0948 199 0.0084 0.0344 0.0000 0.0292
14 refractory filter active 205 15.4738 199 0.0001 0.0721 0.0000 0.0258
15 refractory filter info 205 6.2358 199 0.0133 0.0304 0.0000 0.1783
16 refractory filter nfields_mean 205 4.7762 199 0.0300 0.0234 0.0000 0.2002
17 refractory filter infield_mean 192 1.6125 186 0.2057 0.0086 0.0000 0.1314
18 refractory filter nfields_20pk 205 0.0444 199 0.8333 0.0002 0.0000 0.9642
19 refractory filter infield_20pk 96 0.4618 90 0.4985 0.0051 0.0008 0.6211
32x32 grid, large box cells by genotype and class:
cls interneuron nonspatial place
genotype
Fmr1-null 46 25 46
WT 33 23 40
Means by class and genotype:
coherence active info nfields_mean infield_mean nfields_20pk infield_20pk
cls genotype
interneuron Fmr1-null 0.416 0.821 0.402 8.913 14.963 3.261 19.288
WT 0.615 0.647 0.819 6.242 10.609 2.727 16.444
nonspatial Fmr1-null 0.201 0.222 2.501 3.686 2.135 0.029 5.920
WT 0.206 0.152 3.214 3.000 2.113 0.034 7.140
place Fmr1-null 0.623 0.261 2.266 3.083 3.948 0.861 11.307
WT 0.651 0.240 2.315 2.824 4.531 0.824 11.900
Ranges: coherence active info nfields_mean infield_mean nfields_20pk infield_20pk
min -0.055 0.003 0.041 0.0 0.493 0.0 3.720
max 1.265 0.996 8.740 20.0 54.393 14.0 53.765
Result: not reproduced. In the large box (213 classified clusters) the genotype effect is significant for coherence (F = 9.01, p = 0.0030), active pixels (F = 18.7, p < 0.0001), information (F = 7.27, p = 0.0076) and the number of fields above the mean rate (p = 0.011), with interactions for coherence (p = 0.0077) and active pixels (p = 0.0098); the differences lie mostly in interneurons (coherence 0.42 Fmr1-null against 0.62 WT). In-field rate (p = 0.11 and 0.42 under the two field readings) and the count of fields above 20% of the peak (p = 0.48) show none. The class effect is significant for every measure (p <= 0.0004). The genotype effects on coherence, active pixels and information remain on the 32 x 32 grid (p = 0.0003, 0.0016, 0.0073) and with the refractory filter (p = 0.0084, 0.0001, 0.013).
tables1-place-cell-properties-similar¶
Firing rate, in-field rate, spatial coherence, information content and proportion of active pixels of place cells, and the firing rate of interneurons, do not differ between WT and Fmr1-null mice.
Criterion: a t test per measure, or Mann-Whitney when a sample fails Shapiro-Wilk (Table S1 legend); reproduced when no comparison has p < 0.05. Cells and box as in fig2 (large box), in-field rate under both field readings. Means are compared with Table S1 (tolerances its SEMs).
expected = {("place", "rate"): (1.49, 0.15, 1.27, 0.12), ("place", "infield_mean"): (2.61, 0.2, 2.2, 0.13),
("place", "infield_20pk"): (2.61, 0.2, 2.2, 0.13), ("place", "coherence"): (0.61, 0.02, 0.57, 0.02),
("place", "info"): (1.57, 0.09, 1.5, 0.09), ("place", "active"): (0.45, 0.03, 0.46, 0.03),
("interneuron", "rate"): (16.61, 2.8, 16.02, 1.42)}
rows = []
for variant, bx in [("main", box), ("32x32 grid", box_code), ("refractory filter", box_iso)]:
for (c, m), (ew, sw, ek, sk) in expected.items():
a = bx[(bx.cls == c) & (bx.genotype == "WT")][m]
b = bx[(bx.cls == c) & (bx.genotype == "Fmr1-null")][m]
if min(a.notna().sum(), b.notna().sum()) < 3:
continue
r = hc16.two_group(a, b)
rows.append(dict(variant=variant, cls=c, measure=m, n_WT=r["n_a"], WT=r["mean_a"], WT_sem=r["sem_a"], n_KO=r["n_b"], KO=r["mean_b"],
KO_sem=r["sem_b"], t=r["t"], p_t=r["p_t"], U=r["U"], p_U=r["p_U"], test=r["test_table"],
p_table=r["p_table"], d=r["d"], WT_in_tol=abs(r["mean_a"] - ew) <= sw, KO_in_tol=abs(r["mean_b"] - ek) <= sk))
s1 = pd.DataFrame(rows)
print(s1.round(4).to_string())
print("\nPlace cells by mouse (large box): n, mean rate, coherence, information")
print(box[box.cls == "place"].groupby(["genotype", "mouse"])[["rate", "coherence", "info"]].agg(["count", "mean"]).round(3).to_string())
variant cls measure n_WT WT WT_sem n_KO KO KO_sem t p_t U p_U test p_table d WT_in_tol KO_in_tol
0 main place rate 34 0.9740 0.0924 36 0.8874 0.0824 0.7016 0.4853 659.0 0.5848 t 0.4853 0.1678 False False
1 main place infield_mean 34 4.5313 0.3090 36 3.9482 0.2549 1.4623 0.1483 722.0 0.1982 t 0.1483 0.3497 False False
2 main place infield_20pk 21 11.8996 1.1021 21 11.3072 0.9762 0.4024 0.6896 230.0 0.8209 t 0.6896 0.1242 False False
3 main place coherence 34 0.6515 0.0297 36 0.6226 0.0289 0.6963 0.4886 685.0 0.3942 U 0.3942 0.1665 False False
4 main place info 34 2.3154 0.1118 36 2.2662 0.1520 0.2586 0.7967 674.0 0.4699 U 0.4699 0.0618 False False
5 main place active 34 0.2398 0.0187 36 0.2613 0.0202 -0.7797 0.4383 538.0 0.3878 t 0.4383 -0.1865 False False
6 main interneuron rate 33 5.3984 0.9901 46 10.3430 1.5281 -2.4827 0.0152 501.0 0.0105 U 0.0105 -0.5664 False False
7 32x32 grid place rate 40 0.9345 0.0840 46 0.8324 0.0740 0.9160 0.3623 1015.0 0.4132 U 0.4132 0.1980 False False
8 32x32 grid place infield_mean 40 3.3896 0.2666 46 2.8773 0.2029 1.5507 0.1247 1069.0 0.1985 t 0.1247 0.3352 False False
9 32x32 grid place infield_20pk 33 6.3145 0.4375 33 5.4814 0.5220 1.2233 0.2257 656.0 0.1546 U 0.1546 0.3012 False False
10 32x32 grid place coherence 40 0.8650 0.0415 46 0.7940 0.0384 1.2550 0.2130 1074.0 0.1838 U 0.1838 0.2713 False False
11 32x32 grid place info 40 1.7444 0.0913 46 1.7356 0.1254 0.0554 0.9560 988.0 0.5589 U 0.5589 0.0120 False False
12 32x32 grid place active 40 0.3990 0.0255 46 0.4136 0.0255 -0.4040 0.6872 866.0 0.6432 U 0.6432 -0.0873 False False
13 32x32 grid interneuron rate 33 5.3984 0.9901 46 10.3430 1.5281 -2.4827 0.0152 501.0 0.0105 U 0.0105 -0.5664 False False
14 refractory filter place rate 33 0.9850 0.0945 36 0.8874 0.0824 0.7811 0.4375 646.0 0.5362 t 0.4375 0.1882 False False
15 refractory filter place infield_mean 33 4.5377 0.3184 36 3.9482 0.2549 1.4563 0.1500 700.0 0.2050 t 0.1500 0.3510 False False
16 refractory filter place infield_20pk 21 11.8996 1.1021 21 11.3072 0.9762 0.4024 0.6896 230.0 0.8209 t 0.6896 0.1242 False False
17 refractory filter place coherence 33 0.6559 0.0303 36 0.6226 0.0289 0.7952 0.4293 673.0 0.3457 U 0.3457 0.1916 False False
18 refractory filter place info 33 2.3010 0.1142 36 2.2662 0.1520 0.1804 0.8574 647.0 0.5283 U 0.5283 0.0435 False False
19 refractory filter place active 33 0.2436 0.0188 36 0.2613 0.0202 -0.6376 0.5259 533.0 0.4674 t 0.5259 -0.1537 False False
20 refractory filter interneuron rate 32 5.4539 1.0200 42 9.6665 1.6050 -2.0601 0.0430 479.0 0.0357 U 0.0357 -0.4834 False False
Place cells by mouse (large box): n, mean rate, coherence, information
rate coherence info
count mean count mean count mean
genotype mouse
Fmr1-null M18 4 0.410 4 0.581 4 3.259
M29 7 1.127 7 0.581 7 1.804
M33 3 0.689 3 0.624 3 2.249
M34 9 1.047 9 0.729 9 2.210
M37 11 0.816 11 0.587 11 2.363
M9 2 0.974 2 0.564 2 1.643
WT M1 4 1.065 4 0.596 4 2.045
M11 1 0.455 1 0.636 1 2.647
M16 10 0.938 10 0.718 10 2.670
M28 4 0.988 4 0.632 4 2.400
M3 1 0.459 1 0.810 1 1.825
M32 9 1.155 9 0.566 9 2.064
M35 5 0.843 5 0.704 5 2.237
Result: partially reproduced. Among large-box place cells (34 WT, 36 Fmr1-null) no measure differs: firing rate p = 0.49, in-field rate p = 0.15 (mean-rate fields) and 0.69 (20% of peak), coherence p = 0.39 (Mann-Whitney), information p = 0.47 (Mann-Whitney), active pixels p = 0.44. The interneuron rates differ (WT 5.40, Fmr1-null 10.34 AP/s; Mann-Whitney p = 0.0105; with the refractory filter p = 0.036). No mean is within Table S1's SEM: information is 2.32 and 2.27 bits/spike against 1.57 and 1.5, active pixels 0.24 and 0.26 against 0.45 and 0.46, firing rate 0.97 and 0.89 against 1.49 and 1.27 AP/s. On the 32 x 32 grid (40 and 46 place cells) no place cell measure differs either (p = 0.12 to 0.64), and information (1.74 and 1.74 bits/spike) and active pixels (0.40 and 0.41) come nearer Table S1 without reaching it.
tables1-nonspatial-active-pixels¶
Non-spatial pyramidal cells of Fmr1-null mice discharge in a larger proportion of the environment than those of WT mice (0.40 against 0.26).
Criterion: both values within tolerance (0.26 +/- 0.03, 0.40 +/- 0.05) and the t test (or Mann-Whitney, by Shapiro-Wilk) p < 0.05 with Fmr1-null higher. Large box cells.
a = box[(box.cls == "nonspatial") & (box.genotype == "WT")]["active"]
b = box[(box.cls == "nonspatial") & (box.genotype == "Fmr1-null")]["active"]
r = hc16.two_group(a, b)
print({k: (round(v, 4) if isinstance(v, float) else v) for k, v in r.items()})
print("WT within 0.26 +/- 0.03:", abs(r["mean_a"] - 0.26) <= 0.03, "; Fmr1-null within 0.40 +/- 0.05:", abs(r["mean_b"] - 0.40) <= 0.05)
for label, t in [("R1 (all first stationary sessions)", R1), ("R2", R2), ("large box, 32x32 grid", box_code),
("large box, refractory filter", box_iso)]:
rr = hc16.two_group(t[(t.cls == "nonspatial") & (t.genotype == "WT")]["active"],
t[(t.cls == "nonspatial") & (t.genotype == "Fmr1-null")]["active"])
print(f"Variant {label}: WT {rr['mean_a']:.3f} (n={rr['n_a']}), Fmr1-null {rr['mean_b']:.3f} (n={rr['n_b']}), "
f"t p = {rr['p_t']:.4f}, U p = {rr['p_U']:.4f}, d = {rr['d']:.2f}")
print("By mouse (large box):")
print(box[box.cls == "nonspatial"].groupby(["genotype", "mouse"]).active.agg(["count", "mean"]).round(3).to_string())
{'n_a': 29, 'n_b': 35, 'mean_a': np.float64(0.1519), 'sem_a': np.float64(0.0275), 'mean_b': np.float64(0.2218), 'sem_b': np.float64(0.0309), 't': np.float64(-1.6582), 'df': 62, 'p_t': np.float64(0.1023), 'U': np.float64(385.0), 'p_U': np.float64(0.0999), 'shapiro_a': np.float64(0.0014), 'shapiro_b': np.float64(0.007), 'p_table': np.float64(0.0999), 'test_table': 'U', 'd': -0.4164}
WT within 0.26 +/- 0.03: False ; Fmr1-null within 0.40 +/- 0.05: False
Variant R1 (all first stationary sessions): WT 0.200 (n=60), Fmr1-null 0.250 (n=48), t p = 0.2255, U p = 0.0944, d = -0.24
Variant R2: WT 0.228 (n=91), Fmr1-null 0.280 (n=62), t p = 0.1529, U p = 0.0986, d = -0.24
Variant large box, 32x32 grid: WT 0.241 (n=23), Fmr1-null 0.355 (n=25), t p = 0.1135, U p = 0.0987, d = -0.47
Variant large box, refractory filter: WT 0.147 (n=27), Fmr1-null 0.222 (n=35), t p = 0.0861, U p = 0.0911, d = -0.45
By mouse (large box):
count mean
genotype mouse
Fmr1-null M10 2 0.316
M18 5 0.206
M29 2 0.287
M33 7 0.145
M34 8 0.273
M37 8 0.166
M9 3 0.334
WT M1 1 0.205
M11 2 0.071
M16 12 0.115
M28 2 0.470
M3 1 0.009
M32 6 0.161
M35 5 0.152
Result: not reproduced. Large-box non-spatial pyramidal clusters discharge in 0.152 (WT, n = 29) and 0.222 (Fmr1-null, n = 35) of the visited pixels. Fmr1-null is higher, but both samples fail Shapiro-Wilk and Mann-Whitney gives p = 0.10 (t test p = 0.10), and neither value is within tolerance of 0.26 and 0.40. R1 and R2 give p = 0.09 to 0.23. On the 32 x 32 grid both values are within tolerance (0.241 and 0.355, 23 and 25 cells) but the difference is not significant (Mann-Whitney p = 0.099); with the refractory filter p = 0.091.
Passes and overdispersion¶
STAR Methods, "Overdispersion": for each 5 s interval, exp is the sum over position samples of the time spent times the session's time-averaged rate at that pixel, z = (obs - exp)/sqrt(exp), and the interval is a pass when exp is at least the cell's average rate. The claims file reads this literally, a count against a rate in AP/s; the count expected at the average rate over the 5 s (exp at least the average rate x 5 s) is a variant, and which is meant is asked of a reader (questions/crcns-hc-16.yaml). Intervals are consecutive from the session start (the claims file's reading); a 1 s sliding step is a variant. Place cells of the stationary sessions (large box, stationary arena 1 and 2), each session on its own (R2); R1 (each cell once, from its first session) is a variant.
def pass_table(t, step_s=None, rule="rate", index_fn=hc16.pixel_index):
out = {}
for sid, g in t[t.cls == "place"].groupby("session"):
s = S[sid]
ish = index_fn(s, "Rm")
for c in g.cell:
out[(sid, c)] = hc16.passes(s, c, step_s=step_s, idx_shape=ish, rule=rule)
return out
P_R2 = pass_table(R2)
P_R2_step1 = pass_table(R2, step_s=1.0)
P_R2_count = pass_table(R2, rule="count")
pz = pd.DataFrame([dict(session=k[0], cell=k[1], genotype=S[k[0]].genotype, mouse=S[k[0]].mouse, z=zz)
for k, v in P_R2.items() for zz in v["z"][v["is_pass"]]])
pz["in_R1"] = pz.session.isin(set(R1.session))
pz_count = pd.DataFrame([dict(session=k[0], cell=k[1], genotype=S[k[0]].genotype, mouse=S[k[0]].mouse, z=zz)
for k, v in P_R2_count.items() for zz in v["z"][v["is_pass"]]])
pz_count["in_R1"] = pz_count.session.isin(set(R1.session))
for name, P in [("literal (exp >= mean rate)", P_R2), ("count (exp >= mean rate x 5 s)", P_R2_count)]:
f = np.array([v["is_pass"].mean() for v in P.values()]); n = np.array([v["is_pass"].sum() for v in P.values()])
print(f"Pass rule {name}: fraction of 5 s intervals that pass, median {np.median(f):.3f} (range {f.min():.3f} to {f.max():.3f}); "
f"passes per cell median {np.median(n):.0f} (range {n.min()} to {n.max()})")
print("Place cells with passes:", pz.groupby("genotype")[["session", "cell"]].apply(lambda d: len(d.drop_duplicates())).to_dict())
print("z over passes: range %.2f to %.2f" % (pz.z.min(), pz.z.max()))
Pass rule literal (exp >= mean rate): fraction of 5 s intervals that pass, median 0.763 (range 0.329 to 1.000); passes per cell median 241 (range 93 to 359)
Pass rule count (exp >= mean rate x 5 s): fraction of 5 s intervals that pass, median 0.331 (range 0.167 to 0.460); passes per cell median 109 (range 49 to 165)
Place cells with passes: {'Fmr1-null': 70, 'WT': 55}
z over passes: range -8.48 to 16.87
overdispersion-passes¶
Overdispersion is lower (spatial firing more reliable) in Fmr1-null mice when the unit is a pass through a place field (variance 4.89 against 5.81).
Criterion: both variances within tolerance (5.81 +/- 0.58, 4.89 +/- 0.49), and the F test and Levene's test p < 0.05 with Fmr1-null lower. Pass counts expected 6,776 and 9,102 (+/- 10%).
def od_passes(zz, label):
a = zz[zz.genotype == "WT"].z.to_numpy(); b = zz[zz.genotype == "Fmr1-null"].z.to_numpy()
F = a.var(ddof=1) / b.var(ddof=1)
p_one = stats.f.sf(F, len(a) - 1, len(b) - 1)
lev = stats.levene(a, b, center="mean")
return dict(reading=label, n_WT=len(a), var_WT=a.var(ddof=1), n_KO=len(b), var_KO=b.var(ddof=1), F=F,
p_F_one_sided=p_one, p_F_two_sided=2 * min(p_one, 1 - p_one), levene_F=lev.statistic, levene_p=lev.pvalue,
WT_in_tol=abs(a.var(ddof=1) - 5.81) <= 0.58, KO_in_tol=abs(b.var(ddof=1) - 4.89) <= 0.49,
passes_in_tol=(abs(len(a) - 6776) <= 678) and (abs(len(b) - 9102) <= 910))
pz1 = pd.DataFrame([dict(session=k[0], genotype=S[k[0]].genotype, z=zz)
for k, v in P_R2_step1.items() for zz in v["z"][v["is_pass"]]])
odp = pd.DataFrame([od_passes(pz, "R2, consecutive 5 s"), od_passes(pz[pz.in_R1], "R1, consecutive 5 s"),
od_passes(pz1, "R2, 5 s every 1 s"),
od_passes(pz_count, "R2, count rule"), od_passes(pz_count[pz_count.in_R1], "R1, count rule")])
print(odp.round(4).to_string())
print("\nBy mouse (R2): passes and variance of z")
print(pz.groupby(["genotype", "mouse"]).z.agg(["count", "var"]).round(3).to_string())
print("\nBy mouse (R2, count rule): passes and variance of z")
print(pz_count.groupby(["genotype", "mouse"]).z.agg(["count", "var"]).round(3).to_string())
reading n_WT var_WT n_KO var_KO F p_F_one_sided p_F_two_sided levene_F levene_p WT_in_tol KO_in_tol passes_in_tol
0 R2, consecutive 5 s 12802 3.6903 18521 3.5493 1.0397 0.0082 0.0164 0.0000 0.9977 False False False
1 R1, consecutive 5 s 10747 3.5339 12815 3.8267 0.9235 1.0000 0.0000 20.8587 0.0000 False False False
2 R2, 5 s every 1 s 63865 3.6561 92254 3.5918 1.0179 0.0074 0.0148 0.2564 0.6126 False False False
3 R2, count rule 5442 5.2789 8250 4.6635 1.1319 0.0000 0.0000 11.6845 0.0006 True True False
4 R1, count rule 4609 4.9971 5643 5.1627 0.9679 0.8769 0.2462 4.0172 0.0451 False True False
By mouse (R2): passes and variance of z
count var
genotype mouse
Fmr1-null M18 3452 4.553
M29 2095 3.697
M33 838 2.340
M34 7116 2.971
M37 4494 3.968
M9 526 2.560
WT M1 971 4.099
M11 258 3.604
M16 3345 3.179
M28 1422 5.027
M3 278 3.140
M32 3562 3.808
M35 2262 3.177
M36 704 4.168
By mouse (R2, count rule): passes and variance of z
count var
genotype mouse
Fmr1-null M18 1502 5.825
M29 895 5.639
M33 334 3.412
M34 3293 3.738
M37 1940 5.384
M9 286 2.763
WT M1 425 5.396
M11 124 4.180
M16 1503 4.180
M28 546 8.717
M3 121 4.580
M32 1511 5.591
M35 954 4.192
M36 258 7.293
Result: not reproduced, on the claims file's pass rule. In R2 there are 12,802 WT and 18,521 Fmr1-null passes (expected 6,776 and 9,102) with variances of z 3.69 and 3.55 (expected 5.81 and 4.89). The F test finds the WT variance larger (F = 1.04, one-sided p = 0.008) but Levene's test does not (p = 0.998). In R1 the Fmr1-null variance is the larger (3.83 against 3.53). With 5 s intervals every 1 s the result is as in R2 (Levene p = 0.61). The literal rule passes a median 76% of a cell's 5 s intervals. With the count rule (33% of intervals pass) R2 gives 5,442 and 8,250 passes, variances 5.28 and 4.66, both within tolerance, and Fmr1-null lower by the F test (F = 1.13, one-sided p < 0.0001) and Levene's test (p = 0.0006), which meets the criterion; the WT pass count is outside 6,776 +/- 678. In R1 the count rule gives Fmr1-null the larger variance (5.16 against 5.00). Which rule the paper means is asked of a reader.
overdispersion-cells¶
Overdispersion is marginally lower in Fmr1-null mice when the unit is a place cell (5.02 against 5.98).
Criterion: per cell, the variance of z over its passes; mean +/- SEM per genotype; minimum passes per cell 2, 10 or 30; reproduced when under one reading both means are within tolerance (5.98 +/- 0.44, 5.02 +/- 0.3) and the one-tailed t test gives p < 0.05, Fmr1-null lower.
pc_var = pz.groupby(["session", "cell", "genotype", "in_R1"]).z.agg(["count", "var"]).reset_index()
pc_var_count = pz_count.groupby(["session", "cell", "genotype", "in_R1"]).z.agg(["count", "var"]).reset_index()
rows = []
for label, d0 in [("R2", pc_var), ("R1", pc_var[pc_var.in_R1]), ("R2, count rule", pc_var_count),
("R1, count rule", pc_var_count[pc_var_count.in_R1])]:
for mn in [2, 10, 30]:
d = d0[d0["count"] >= mn]
r = hc16.two_group(d[d.genotype == "WT"]["var"], d[d.genotype == "Fmr1-null"]["var"], alternative="greater")
rows.append(dict(reading=label, min_passes=mn, n_WT=r["n_a"], WT=r["mean_a"], WT_sem=r["sem_a"], n_KO=r["n_b"],
KO=r["mean_b"], KO_sem=r["sem_b"], t=r["t"], p_t_one=r["p_t"], U=r["U"], p_U_one=r["p_U"], d=r["d"],
WT_in_tol=abs(r["mean_a"] - 5.98) <= 0.44, KO_in_tol=abs(r["mean_b"] - 5.02) <= 0.3))
odc = pd.DataFrame(rows)
print(odc.round(4).to_string())
print("Per-cell variance range: %.2f to %.2f" % (pc_var["var"].min(), pc_var["var"].max()))
print("Passes per cell, count rule: min %d, median %.0f" % (pc_var_count["count"].min(), pc_var_count["count"].median()))
reading min_passes n_WT WT WT_sem n_KO KO KO_sem t p_t_one U p_U_one d WT_in_tol KO_in_tol 0 R2 2 55 3.6828 0.2409 70 3.5280 0.2134 0.4810 0.3157 2047.0 0.2728 0.0867 False False 1 R2 10 55 3.6828 0.2409 70 3.5280 0.2134 0.4810 0.3157 2047.0 0.2728 0.0867 False False 2 R2 30 55 3.6828 0.2409 70 3.5280 0.2134 0.4810 0.3157 2047.0 0.2728 0.0867 False False 3 R1 2 48 3.5634 0.2524 51 3.7174 0.2533 -0.4303 0.6660 1163.0 0.6666 -0.0865 False False 4 R1 10 48 3.5634 0.2524 51 3.7174 0.2533 -0.4303 0.6660 1163.0 0.6666 -0.0865 False False 5 R1 30 48 3.5634 0.2524 51 3.7174 0.2533 -0.4303 0.6660 1163.0 0.6666 -0.0865 False False 6 R2, count rule 2 55 5.3285 0.4593 70 4.6888 0.3268 1.1650 0.1231 2125.0 0.1605 0.2099 False False 7 R2, count rule 10 55 5.3285 0.4593 70 4.6888 0.3268 1.1650 0.1231 2125.0 0.1605 0.2099 False False 8 R2, count rule 30 55 5.3285 0.4593 70 4.6888 0.3268 1.1650 0.1231 2125.0 0.1605 0.2099 False False 9 R1, count rule 2 48 5.0217 0.4424 51 5.0779 0.3982 -0.0946 0.5376 1188.0 0.6009 -0.0190 False True 10 R1, count rule 10 48 5.0217 0.4424 51 5.0779 0.3982 -0.0946 0.5376 1188.0 0.6009 -0.0190 False True 11 R1, count rule 30 48 5.0217 0.4424 51 5.0779 0.3982 -0.0946 0.5376 1188.0 0.6009 -0.0190 False True Per-cell variance range: 1.05 to 10.93 Passes per cell, count rule: min 49, median 109
Result: not reproduced. Every place cell has at least 30 passes, so the three minimums give the same sample. R2: WT 3.68 +/- 0.24 (n = 55), Fmr1-null 3.53 +/- 0.21 (n = 70), one-tailed t p = 0.32, Mann-Whitney p = 0.27; R1: 3.56 against 3.72 (p = 0.67). Neither mean is within tolerance of 5.98 and 5.02. With the count rule (at least 49 passes per cell, again one sample) R2 gives 5.33 +/- 0.46 against 4.69 +/- 0.33 (one-tailed t p = 0.12) and R1 5.02 against 5.08 (p = 0.54); in no reading are both means within tolerance.
fig4b-covariance-30-passes and fig4b-covariance-40-passes¶
Pairs of Fmr1-null place cells with overlapping fields covary less in their pass-by-pass discharge than WT pairs (average r 0.04 against 0.09), a difference that disappears after shuffling. / With at least 40 passes, the difference holds and is larger (0.06 against 0.19).
Criterion: for each pair of simultaneously recorded place cells, the Pearson correlation of the two cells' z over the intervals that are passes for both, pairs with at least 30 (40) such passes; t test on Fisher z; averages within tolerance (WT 0.09 +/- 0.03, Fmr1-null 0.04 +/- 0.01; for 40, 0.19 +/- 0.04 and 0.06 +/- 0.01); the shuffle permutes z within each cell of a pair (1 permutation per pair; 100 as a variant).
rng_cov = np.random.default_rng(404)
def pair_cov(P, nshuf=100, rng=rng_cov):
rows = []
by_sess = {}
for (sid, c), v in P.items():
by_sess.setdefault(sid, []).append((c, v))
for sid, lst in by_sess.items():
for i in range(len(lst)):
for j in range(i + 1, len(lst)):
(ca, va), (cb, vb) = lst[i], lst[j]
both = va["is_pass"] & vb["is_pass"]
n = int(both.sum())
if n < 3:
continue
za, zb = va["z"][both], vb["z"][both]
r = stats.pearsonr(za, zb).statistic if za.std() > 0 and zb.std() > 0 else np.nan
sh = [stats.pearsonr(rng.permutation(za), rng.permutation(zb)).statistic for _ in range(nshuf)]
rows.append(dict(session=sid, genotype=S[sid].genotype, mouse=S[sid].mouse, a=ca, b=cb, n=n, r=r,
r_shuf1=sh[0], r_shuf100=np.mean(hc16.fisher(np.array(sh))), r_shuf_all=np.array(sh)))
return pd.DataFrame(rows)
cov = pair_cov(P_R2)
cov["in_R1"] = cov.session.isin(set(R1.session))
cov_count = pair_cov(P_R2_count)
cov_count["in_R1"] = cov_count.session.isin(set(R1.session))
for name, d in [("literal rule", cov), ("count rule", cov_count)]:
print(f"Joint passes per pair, {name}: percentiles 0/10/50/90/100 =", np.percentile(d.n, [0, 10, 50, 90, 100]).round(0).tolist(),
"; pairs with >= 30:", int((d.n >= 30).sum()), "and >= 40:", int((d.n >= 40).sum()), "of", len(d))
print("Pairs with >= 3 joint passes:", cov.groupby("genotype").size().to_dict(), "; joint passes range", cov.n.min(), "to", cov.n.max())
rows = []
for label, d0 in [("R2", cov), ("R1", cov[cov.in_R1]), ("R2, count rule", cov_count), ("R1, count rule", cov_count[cov_count.in_R1])]:
for mn, exp in [(30, (0.09, 0.03, 0.04, 0.01)), (40, (0.19, 0.04, 0.06, 0.01))]:
d = d0[(d0.n >= mn) & d0.r.notna()]
a = d[d.genotype == "WT"]; b = d[d.genotype == "Fmr1-null"]
t = stats.ttest_ind(hc16.fisher(a.r), hc16.fisher(b.r))
u = stats.mannwhitneyu(a.r, b.r)
t1 = stats.ttest_ind(hc16.fisher(a.r_shuf1), hc16.fisher(b.r_shuf1))
t100 = stats.ttest_ind(a.r_shuf100, b.r_shuf100)
rows.append(dict(reading=label, min_passes=mn, n_WT=len(a), r_WT=a.r.mean(), sem_WT=a.r.sem(), n_KO=len(b),
r_KO=b.r.mean(), sem_KO=b.r.sem(), t=t.statistic, p_t=t.pvalue, U=u.statistic, p_U=u.pvalue,
d=hc16.cohen_d(hc16.fisher(a.r), hc16.fisher(b.r)),
shuf1_t=t1.statistic, shuf1_p=t1.pvalue, shuf100_t=t100.statistic, shuf100_p=t100.pvalue,
WT_in_tol=abs(a.r.mean() - exp[0]) <= exp[1], KO_in_tol=abs(b.r.mean() - exp[2]) <= exp[3]))
cv = pd.DataFrame(rows)
print(cv.round(4).to_string())
print("\nEach of the 100 permutations as a single-permutation test (t on Fisher z of the permuted r):")
for label, d0 in [("R2", cov), ("R2, count rule", cov_count)]:
for mn in [30, 40]:
d = d0[(d0.n >= mn) & d0.r.notna()]
a = np.vstack(d[d.genotype == "WT"].r_shuf_all); b = np.vstack(d[d.genotype == "Fmr1-null"].r_shuf_all)
tt = stats.ttest_ind(hc16.fisher(a), hc16.fisher(b), axis=0)
print(f" {label}, >= {mn}: p < 0.05 in {int((tt.pvalue < 0.05).sum())} of 100, p < 0.01 in {int((tt.pvalue < 0.01).sum())}; "
f"|t| median {np.median(np.abs(tt.statistic)):.2f}, max {np.abs(tt.statistic).max():.2f}; "
f"the first permutation (shuf1 above) t = {tt.statistic[0]:.2f}, ranked {int((np.abs(tt.statistic) >= abs(tt.statistic[0])).sum())} of 100 by |t|")
print("\nBy mouse (R2, >= 30 joint passes): pairs and mean r")
print(cov[cov.n >= 30].groupby(["genotype", "mouse"]).r.agg(["count", "mean"]).round(3).to_string())
print("\nBy mouse (R2, count rule, >= 30 joint passes): pairs and mean r")
print(cov_count[cov_count.n >= 30].groupby(["genotype", "mouse"]).r.agg(["count", "mean"]).round(3).to_string())
fig, ax = plt.subplots(figsize=(4, 3))
for g, col in [("WT", "k"), ("Fmr1-null", "r")]:
v = np.sort(cov[(cov.n >= 30) & (cov.genotype == g)].r.dropna())
ax.plot(v, np.arange(1, len(v) + 1) / len(v), color=col, label=g)
ax.set_xlabel("pair r over joint passes (>= 30)"); ax.set_ylabel("cumulative fraction"); ax.legend()
plt.show()
print("Plotted r range: %.3f to %.3f" % (cov[cov.n >= 30].r.min(), cov[cov.n >= 30].r.max()))
Joint passes per pair, literal rule: percentiles 0/10/50/90/100 = [39.0, 99.0, 177.0, 304.0, 346.0] ; pairs with >= 30: 332 and >= 40: 331 of 332
Joint passes per pair, count rule: percentiles 0/10/50/90/100 = [3.0, 9.0, 28.0, 73.0, 129.0] ; pairs with >= 30: 153 and >= 40: 116 of 328
Pairs with >= 3 joint passes: {'Fmr1-null': 210, 'WT': 122} ; joint passes range 39 to 346
reading min_passes n_WT r_WT sem_WT n_KO r_KO sem_KO t p_t U p_U d shuf1_t shuf1_p shuf100_t shuf100_p WT_in_tol KO_in_tol
0 R2 30 122 0.0608 0.0198 210 0.0210 0.0104 2.2890 0.0227 13752.0 0.2642 0.2606 1.2479 0.2130 1.2306 0.2193 True False
1 R2 40 122 0.0608 0.0198 209 0.0200 0.0104 2.3264 0.0206 13737.0 0.2397 0.2651 1.2082 0.2279 1.1428 0.2539 False False
2 R1 30 118 0.0611 0.0204 152 0.0170 0.0129 2.1393 0.0333 9754.0 0.2171 0.2625 0.7540 0.4515 0.8277 0.4086 True False
3 R1 40 118 0.0611 0.0204 151 0.0155 0.0129 2.1837 0.0299 9739.0 0.1902 0.2683 0.7049 0.4815 0.7151 0.4751 False False
4 R2, count rule 30 50 0.1646 0.0492 103 0.0074 0.0240 3.5426 0.0005 3212.0 0.0133 0.6106 3.6056 0.0004 0.6667 0.5060 False False
5 R2, count rule 40 30 0.2786 0.0662 86 0.0394 0.0256 4.4072 0.0000 1800.0 0.0013 0.9345 1.8708 0.0639 -0.0557 0.9557 False False
6 R1, count rule 30 48 0.1662 0.0509 63 0.0103 0.0331 2.8635 0.0050 1868.0 0.0343 0.5486 3.0207 0.0031 0.4445 0.6575 False False
7 R1, count rule 40 28 0.2894 0.0698 50 0.0480 0.0375 3.5108 0.0008 968.0 0.0053 0.8287 1.4744 0.1445 -0.3353 0.7383 False False
Each of the 100 permutations as a single-permutation test (t on Fisher z of the permuted r):
R2, >= 30: p < 0.05 in 4 of 100, p < 0.01 in 2; |t| median 0.67, max 3.22; the first permutation (shuf1 above) t = 1.25, ranked 19 of 100 by |t|
R2, >= 40: p < 0.05 in 3 of 100, p < 0.01 in 2; |t| median 0.67, max 3.18; the first permutation (shuf1 above) t = 1.21, ranked 20 of 100 by |t|
R2, count rule, >= 30: p < 0.05 in 4 of 100, p < 0.01 in 2; |t| median 0.68, max 3.61; the first permutation (shuf1 above) t = 3.61, ranked 1 of 100 by |t|
R2, count rule, >= 40: p < 0.05 in 5 of 100, p < 0.01 in 2; |t| median 0.71, max 2.86; the first permutation (shuf1 above) t = 1.87, ranked 8 of 100 by |t|
By mouse (R2, >= 30 joint passes): pairs and mean r
count mean
genotype mouse
Fmr1-null M18 22 0.036
M29 21 -0.001
M33 3 0.028
M34 102 0.036
M37 61 -0.005
M9 1 0.207
WT M1 6 0.067
M16 52 0.091
M28 7 -0.030
M32 45 0.049
M35 11 0.008
M36 1 0.205
By mouse (R2, count rule, >= 30 joint passes): pairs and mean r
count mean
genotype mouse
Fmr1-null M18 8 -0.013
M29 8 0.002
M33 1 0.040
M34 61 0.019
M37 24 -0.025
M9 1 0.261
WT M1 3 0.193
M16 18 0.252
M28 1 0.304
M32 20 0.150
M35 7 -0.065
M36 1 0.276
Plotted r range: -0.534 to 0.965
Result: partially reproduced, for both claims. fig4b-covariance-30-passes: in R2, 122 WT and 210 Fmr1-null pairs have at least 30 joint passes (expected 109 and 249). WT r = 0.061 +/- 0.020 and Fmr1-null 0.021 +/- 0.010; t on Fisher z = 2.29, p = 0.023 (Mann-Whitney p = 0.26). WT is within tolerance of 0.09 +/- 0.03, Fmr1-null is not within 0.04 +/- 0.01. After one permutation per pair the difference is gone (p = 0.21; mean of 100 permutations p = 0.22). R1 gives the same pattern (p = 0.033).
fig4b-covariance-40-passes, partially reproduced: under the literal pass rule the fewest joint passes of any pair is 39, so the 30 and 40 pass minimums select nothing and the sample hardly changes (122 WT, 209 Fmr1-null pairs): r = 0.061 against 0.020, t = 2.33, p = 0.021. The difference holds but is not larger, and both averages are outside tolerance (0.19 +/- 0.04, 0.06 +/- 0.01).
With the count rule pairs have 3 to 129 joint passes (median 28), and the minimums select: at 30, 50 WT and 103 Fmr1-null pairs, r = 0.165 against 0.007, t = 3.54, p = 0.0005; at 40, 30 and 86 pairs, r = 0.279 against 0.039, t = 4.41, p < 0.0001, a larger difference, as claimed. After 100 permutations neither differs (p = 0.51, 0.96), but one permutation per pair still differs at 30 passes (p = 0.0004). That draw is a chance one: taken one at a time, the 100 permutations give p < 0.05 in 4 of 100 at 30 passes (5 at 40), as a null should, and the first permutation, the one reported, has the largest |t| of the 100 (3.61; median 0.68). Under the literal rule 4 and 3 of 100 give p < 0.05. No average is within tolerance under the count rule (WT above, Fmr1-null below), so both outcomes would stay partially reproduced.
Kendall correlations¶
STAR Methods, "Functional Coupling of Cell Pairs": Kendall's correlation between the spike count series of each pair of simultaneously recorded cells, at 25, 40, 125, 250 ms, 1 s and 5 s. Tau-b, as in the CorrelateBPFSpikeTrainEpisodes code. The claims file names the readings: K1, every bin; K2, only bins in which the mouse is in an active pixel (rate at least the cell's mean, 32 x 32 map) of both cells; P1, the whole session; P2, the first 5 min; a pair counted once (R1 sessions) or per session (R2). Bins are placed on the spike clock from the first position sample; the position of a bin is the sample at its centre (hc16.Session.sample_time). Pairs are computed for every stationary session (R2) and the periods: the session, the first 5 min, the two halves and the second 5 min.
T_K = time.time()
def session_taus(t, cls, gate, extra_periods=True):
out = []
for sid, g in t[t.cls == cls].groupby("session"):
cl = list(g.cell)
if len(cl) < 2:
continue
s = S[sid]
D = s.sample_time[-1] - s.sample_time[0]
periods = {"session": (None, None), "first5": (0, 300)}
if extra_periods:
periods.update({"half1": (0, D / 2), "half2": (D / 2, D), "second5": (300, 600)})
out.append(hc16.pair_taus(s, cl, periods=periods, gate=gate))
d = pd.concat(out, ignore_index=True)
d["in_R1"] = d.session.isin(set(R1.session))
d["K"] = "K2" if gate else "K1"
return d
KT_place = pd.concat([session_taus(R2, "place", False), session_taus(R2, "place", True)], ignore_index=True)
KT_ns = pd.concat([session_taus(R2, "nonspatial", False), session_taus(R2, "nonspatial", True)], ignore_index=True)
print(f"Kendall computed in {time.time() - T_K:.0f} s")
for name, d in [("place", KT_place), ("non-spatial", KT_ns)]:
q = d[(d.period == "session") & (d.width == 0.025) & (d.K == "K1")]
print(f"{name} pairs (R2): {q.groupby('genotype').size().to_dict()}; in R1 sessions: {q[q.in_R1].groupby('genotype').size().to_dict()}")
print(" tau range over all readings %.3f to %.3f; undefined tau: %d of %d" % (d.tau.min(), d.tau.max(), d.tau.isna().sum(), len(d)))
print(" median bins entering K2 (session):", d[(d.K == "K2") & (d.period == "session")].groupby("width").nbins.median().to_dict())
def mw_table(d, expected=None):
rows = []
for (K, period, unit), _ in [((K, p, u), None) for K in ["K1", "K2"] for p in ["session", "first5"] for u in ["R1", "R2"]]:
q = d[(d.K == K) & (d.period == period)]
if unit == "R1":
q = q[q.in_R1]
for w in hc16.TIMESCALES:
qq = q[q.width == w].dropna(subset=["tau"])
a = qq[qq.genotype == "WT"].tau; b = qq[qq.genotype == "Fmr1-null"].tau
u = stats.mannwhitneyu(a, b)
row = dict(K=K, period=period, unit=unit, width=w, n_WT=len(a), WT=a.mean(), WT_sem=a.sem(), n_KO=len(b),
KO=b.mean(), KO_sem=b.sem(), U=u.statistic, p=u.pvalue, KO_lower=b.mean() < a.mean(),
d=hc16.cohen_d(a, b))
if expected and w in expected:
ew, sw, ek, sk = expected[w]
row["WT_in_tol"] = abs(a.mean() - ew) <= sw; row["KO_in_tol"] = abs(b.mean() - ek) <= sk
rows.append(row)
return pd.DataFrame(rows)
Kendall computed in 11 s
place pairs (R2): {'Fmr1-null': 210, 'WT': 122}; in R1 sessions: {'Fmr1-null': 152, 'WT': 118}
tau range over all readings -1.000 to 1.000; undefined tau: 845 of 19920
median bins entering K2 (session): {0.025: 3708.5, 0.04: 2325.0, 0.125: 741.5, 0.25: 366.5, 1.0: 93.5, 5.0: 18.0}
non-spatial pairs (R2): {'Fmr1-null': 146, 'WT': 380}; in R1 sessions: {'Fmr1-null': 115, 'WT': 205}
tau range over all readings -1.000 to 1.000; undefined tau: 2759 of 31560
median bins entering K2 (session): {0.025: 3960.5, 0.04: 2476.5, 0.125: 799.5, 0.25: 398.5, 1.0: 102.0, 5.0: 19.0}
fig4cd-kendall-place-cells¶
Kendall correlations between Fmr1-null place cell spike trains are weaker than between WT ones at the 25 ms, 40 ms and 125 ms timescales, but not significantly at 1 s and 5 s.
Criterion: Mann-Whitney between genotypes, pairs as units; reproduced when, under one reading, 25, 40 and 125 ms give p < 0.05 with Fmr1-null lower and 1 s and 5 s give p >= 0.05; partially reproduced when two of the three short timescales do. Pair counts expected 247 and 504.
exp_pc = {0.025: (0.017, 0.006, 0.009, 0.0012), 0.040: (0.02, 0.007, 0.01, 0.002), 0.125: (0.02, 0.008, 0.017, 0.003)}
kp = mw_table(KT_place, exp_pc)
print(kp.round(4).to_string())
def judge(kt, short, long_):
out = []
for key, g in kt.groupby(["K", "period", "unit"]):
g = g.set_index("width")
sig_short = [(g.loc[w, "p"] < 0.05) and g.loc[w, "KO_lower"] for w in short]
ns_long = [g.loc[w, "p"] >= 0.05 for w in long_]
out.append(dict(K=key[0], period=key[1], unit=key[2], short_sig=sum(sig_short), long_ns=sum(ns_long),
n_WT=int(g.n_WT.iloc[0]), n_KO=int(g.n_KO.iloc[0])))
return pd.DataFrame(out)
print("\nPer reading: short timescales with p < 0.05 and Fmr1-null lower; long timescales with p >= 0.05")
print(judge(kp, [0.025, 0.040, 0.125], [1.0, 5.0]).to_string())
print("\nBy mouse (K1, session, R2): pairs and mean tau at 25 ms")
q = KT_place[(KT_place.K == "K1") & (KT_place.period == "session") & (KT_place.width == 0.025)]
print(q.groupby(["genotype", "mouse"]).tau.agg(["count", "mean"]).round(4).to_string())
print("\nWithout M16 (52 of the 122 WT pairs): per reading, and K1 over the session")
kp16 = mw_table(KT_place[KT_place.mouse != "M16"], exp_pc)
print(judge(kp16, [0.025, 0.040, 0.125], [1.0, 5.0]).to_string())
print(kp16[(kp16.K == "K1") & (kp16.period == "session")].round(4).to_string())
print("Short timescales with p < 0.05 without M16, any reading:")
print(kp16[(kp16.width <= 0.125) & (kp16.p < 0.05)].round(4).to_string())
K period unit width n_WT WT WT_sem n_KO KO KO_sem U p KO_lower d WT_in_tol KO_in_tol
0 K1 session R1 0.025 118 0.0305 0.0122 152 0.0045 0.0024 9466.0 0.4344 True 0.2878 False False
1 K1 session R1 0.040 118 0.0334 0.0127 152 0.0057 0.0030 9229.0 0.6823 True 0.2909 False False
2 K1 session R1 0.125 118 0.0385 0.0143 152 0.0049 0.0054 9722.0 0.2364 True 0.2944 False False
3 K1 session R1 0.250 118 0.0411 0.0153 152 0.0048 0.0072 9806.0 0.1882 True 0.2819 NaN NaN
4 K1 session R1 1.000 118 0.0515 0.0176 152 0.0023 0.0118 10409.0 0.0236 True 0.2941 NaN NaN
5 K1 session R1 5.000 118 0.0801 0.0198 152 0.0009 0.0162 11046.0 0.0011 True 0.3833 NaN NaN
6 K1 session R2 0.025 122 0.0294 0.0118 210 0.0067 0.0022 12674.0 0.8723 True 0.2732 False False
7 K1 session R2 0.040 122 0.0321 0.0123 210 0.0084 0.0028 12447.0 0.6673 True 0.2693 False True
8 K1 session R2 0.125 122 0.0368 0.0138 210 0.0082 0.0046 13249.0 0.6030 True 0.2684 False False
9 K1 session R2 0.250 122 0.0394 0.0149 210 0.0079 0.0061 13495.0 0.4169 True 0.2584 NaN NaN
10 K1 session R2 1.000 122 0.0493 0.0171 210 0.0059 0.0099 14468.0 0.0493 True 0.2689 NaN NaN
11 K1 session R2 5.000 122 0.0770 0.0193 210 0.0032 0.0131 15535.0 0.0012 True 0.3712 NaN NaN
12 K1 first5 R1 0.025 118 0.0278 0.0124 152 0.0031 0.0021 8861.0 0.8671 True 0.2721 False False
13 K1 first5 R1 0.040 118 0.0296 0.0127 152 0.0024 0.0026 9037.0 0.9143 True 0.2879 False False
14 K1 first5 R1 0.125 118 0.0378 0.0147 152 0.0043 0.0051 9488.0 0.4144 True 0.2890 False False
15 K1 first5 R1 0.250 118 0.0410 0.0158 152 0.0054 0.0069 9495.0 0.4081 True 0.2735 NaN NaN
16 K1 first5 R1 1.000 118 0.0546 0.0178 152 0.0060 0.0110 10277.0 0.0398 True 0.2985 NaN NaN
17 K1 first5 R1 5.000 118 0.0836 0.0203 152 -0.0068 0.0162 11137.0 0.0007 True 0.4317 NaN NaN
18 K1 first5 R2 0.025 122 0.0267 0.0120 210 0.0052 0.0020 12197.0 0.4676 True 0.2586 False False
19 K1 first5 R2 0.040 122 0.0284 0.0123 210 0.0061 0.0025 12300.0 0.5457 True 0.2561 False False
20 K1 first5 R2 0.125 122 0.0356 0.0143 210 0.0077 0.0045 12924.0 0.8929 True 0.2570 False False
21 K1 first5 R2 0.250 122 0.0385 0.0153 210 0.0083 0.0060 13120.0 0.7136 True 0.2446 NaN NaN
22 K1 first5 R2 1.000 122 0.0519 0.0173 210 0.0093 0.0097 14482.0 0.0474 True 0.2645 NaN NaN
23 K1 first5 R2 5.000 122 0.0805 0.0199 210 -0.0039 0.0135 15664.0 0.0007 True 0.4131 NaN NaN
24 K2 session R1 0.025 118 0.0279 0.0137 149 -0.0000 0.0034 8987.0 0.7551 True 0.2698 False False
25 K2 session R1 0.040 118 0.0281 0.0144 149 0.0001 0.0044 8748.0 0.9459 True 0.2510 False False
26 K2 session R1 0.125 118 0.0249 0.0164 149 -0.0101 0.0076 9226.0 0.4881 True 0.2555 True False
27 K2 session R1 0.250 118 0.0130 0.0183 149 -0.0206 0.0106 9007.0 0.7309 True 0.2054 NaN NaN
28 K2 session R1 1.000 118 0.0144 0.0228 145 -0.0442 0.0154 9727.0 0.0562 True 0.2721 NaN NaN
29 K2 session R1 5.000 110 0.0396 0.0302 136 -0.0036 0.0270 7797.5 0.5678 True 0.1370 NaN NaN
30 K2 session R2 0.025 122 0.0268 0.0132 207 0.0042 0.0031 12163.0 0.5781 True 0.2363 False False
31 K2 session R2 0.040 122 0.0269 0.0140 207 0.0060 0.0039 11799.0 0.3207 True 0.2008 True False
32 K2 session R2 0.125 122 0.0236 0.0159 207 -0.0034 0.0064 12667.0 0.9622 True 0.2085 True False
33 K2 session R2 0.250 122 0.0120 0.0177 207 -0.0145 0.0086 12554.0 0.9307 True 0.1710 NaN NaN
34 K2 session R2 1.000 122 0.0132 0.0221 203 -0.0371 0.0123 13949.0 0.0563 True 0.2467 NaN NaN
35 K2 session R2 5.000 114 0.0449 0.0296 193 -0.0004 0.0205 11519.5 0.4906 True 0.1528 NaN NaN
36 K2 first5 R1 0.025 113 0.0224 0.0144 135 0.0021 0.0069 7767.0 0.8049 True 0.1701 True False
37 K2 first5 R1 0.040 113 0.0227 0.0153 135 -0.0016 0.0077 7979.0 0.5327 True 0.1901 True False
38 K2 first5 R1 0.125 113 0.0237 0.0192 132 0.0049 0.0138 7533.5 0.8921 True 0.1036 True False
39 K2 first5 R1 0.250 109 0.0084 0.0253 130 -0.0126 0.0194 7162.0 0.8857 True 0.0868 NaN NaN
40 K2 first5 R1 1.000 106 0.0489 0.0349 115 -0.0146 0.0287 6658.5 0.2358 True 0.1902 NaN NaN
41 K2 first5 R1 5.000 62 0.0399 0.0588 73 0.0042 0.0552 2361.5 0.6650 True 0.0764 NaN NaN
42 K2 first5 R2 0.025 117 0.0218 0.0140 191 0.0050 0.0053 10865.0 0.6847 True 0.1527 True False
43 K2 first5 R2 0.040 117 0.0220 0.0148 191 0.0054 0.0060 10889.0 0.7081 True 0.1397 True False
44 K2 first5 R2 0.125 117 0.0229 0.0187 188 0.0076 0.0106 10732.5 0.7235 True 0.0906 True False
45 K2 first5 R2 0.250 113 0.0091 0.0246 186 -0.0113 0.0146 10490.0 0.9796 True 0.0910 NaN NaN
46 K2 first5 R2 1.000 110 0.0426 0.0338 170 -0.0084 0.0218 9933.5 0.3783 True 0.1624 NaN NaN
47 K2 first5 R2 5.000 65 0.0511 0.0567 122 0.0219 0.0396 4077.0 0.7516 True 0.0657 NaN NaN
Per reading: short timescales with p < 0.05 and Fmr1-null lower; long timescales with p >= 0.05
K period unit short_sig long_ns n_WT n_KO
0 K1 first5 R1 0 0 118 152
1 K1 first5 R2 0 0 122 210
2 K1 session R1 0 0 118 152
3 K1 session R2 0 0 122 210
4 K2 first5 R1 0 2 113 135
5 K2 first5 R2 0 2 117 191
6 K2 session R1 0 2 118 149
7 K2 session R2 0 2 122 207
By mouse (K1, session, R2): pairs and mean tau at 25 ms
count mean
genotype mouse
Fmr1-null M18 22 0.0182
M29 21 0.0080
M33 3 0.0074
M34 102 0.0083
M37 61 -0.0011
M9 1 0.0349
WT M1 6 0.0220
M16 52 0.0522
M28 7 0.0020
M32 45 0.0134
M35 11 0.0100
M36 1 0.0062
Without M16 (52 of the 122 WT pairs): per reading, and K1 over the session
K period unit short_sig long_ns n_WT n_KO
0 K1 first5 R1 1 0 67 152
1 K1 first5 R2 0 0 70 210
2 K1 session R1 0 0 67 152
3 K1 session R2 0 0 70 210
4 K2 first5 R1 0 2 63 135
5 K2 first5 R2 0 2 66 191
6 K2 session R1 0 2 67 149
7 K2 session R2 0 2 70 207
K period unit width n_WT WT WT_sem n_KO KO KO_sem U p KO_lower d WT_in_tol KO_in_tol
0 K1 session R1 0.025 67 0.0131 0.0053 152 0.0045 0.0024 5408.0 0.4653 True 0.2524 True False
1 K1 session R1 0.040 67 0.0171 0.0068 152 0.0057 0.0030 5383.0 0.5014 True 0.2608 True False
2 K1 session R1 0.125 67 0.0259 0.0107 152 0.0049 0.0054 5824.0 0.0905 True 0.2860 True False
3 K1 session R1 0.250 67 0.0326 0.0134 152 0.0048 0.0072 5906.0 0.0597 True 0.2905 NaN NaN
4 K1 session R1 1.000 67 0.0525 0.0192 152 0.0023 0.0118 6191.0 0.0110 True 0.3365 NaN NaN
5 K1 session R1 5.000 67 0.0775 0.0241 152 0.0009 0.0162 6327.0 0.0043 True 0.3855 NaN NaN
6 K1 session R2 0.025 70 0.0124 0.0051 210 0.0067 0.0022 7334.0 0.9789 True 0.1630 True False
7 K1 session R2 0.040 70 0.0161 0.0065 210 0.0084 0.0028 7353.0 0.9966 True 0.1751 True True
8 K1 session R2 0.125 70 0.0242 0.0103 210 0.0082 0.0046 8001.0 0.2675 True 0.2214 True False
9 K1 session R2 0.250 70 0.0303 0.0129 210 0.0079 0.0061 8180.0 0.1574 True 0.2389 NaN NaN
10 K1 session R2 1.000 70 0.0482 0.0185 210 0.0059 0.0099 8662.0 0.0254 True 0.2900 NaN NaN
11 K1 session R2 5.000 70 0.0728 0.0233 210 0.0032 0.0131 8967.0 0.0059 True 0.3637 NaN NaN
Short timescales with p < 0.05 without M16, any reading:
K period unit width n_WT WT WT_sem n_KO KO KO_sem U p KO_lower d WT_in_tol KO_in_tol
14 K1 first5 R1 0.125 67 0.0269 0.0111 152 0.0043 0.0051 5957.0 0.0454 True 0.3109 True False
Result: not reproduced. Under no reading do the 25, 40 and 125 ms comparisons reach p < 0.05 (smallest p over the eight readings 0.24). With K1 over the session (R2, 122 WT and 210 Fmr1-null pairs, expected 247 and 504) mean tau at 25 ms is 0.029 against 0.0067, lower in Fmr1-null, but Mann-Whitney p = 0.87, 0.67 and 0.60 at 25, 40 and 125 ms, while 1 s (p = 0.049) and 5 s (p = 0.0012) do differ, the reverse of the claimed pattern. No mean tau at 25 ms is within tolerance under the K1 readings. Without M16, which gives 52 of the 122 WT pairs, the pattern is the same (K1 over the session, R2: p = 0.98, 1.00 and 0.27 at 25, 40 and 125 ms; 0.025 and 0.0059 at 1 s and 5 s), so it is not one mouse's; one of the eight readings without M16 has one short timescale below 0.05 (K1, first 5 min, R1, 125 ms, p = 0.045).
fig4d-network-state-recurrence¶
Network states recur less in Fmr1-null ensembles; the vector of pairwise Kendall correlations does not differ between the two halves of WT recordings but does in Fmr1-null recordings.
Criterion: for each pair, tau in the earlier and later interval; a paired t test per genotype and timescale, and PCo, the Pearson correlation of the two vectors (all pairs of a genotype pooled). Readings: H1 the two halves, H2 the first two 5 min intervals, each with K1 and K2. Reproduced when, under one reading, no WT test has p < 0.05, at least five of the six Fmr1-null tests do, and WT PCo exceeds Fmr1-null PCo at most timescales. Pairs per session (R2), as the paper's 247 and 504 pairs; R1 shown too.
rows = []
for K in ["K1", "K2"]:
for H, (p1, p2) in [("H1", ("half1", "half2")), ("H2", ("first5", "second5"))]:
for unit in ["R2", "R1"]:
for g in ["WT", "Fmr1-null"]:
for w in hc16.TIMESCALES:
q = KT_place[(KT_place.K == K) & (KT_place.genotype == g) & (KT_place.width == w)]
if unit == "R1":
q = q[q.in_R1]
a = q[q.period == p1].set_index(["session", "a", "b"]).tau
b = q[q.period == p2].set_index(["session", "a", "b"]).tau
j = pd.concat([a, b], axis=1, keys=["x", "y"]).dropna()
t = stats.ttest_rel(j.x, j.y)
rows.append(dict(K=K, H=H, unit=unit, genotype=g, width=w, n=len(j), t=t.statistic, p=t.pvalue,
PCo=stats.pearsonr(j.x, j.y).statistic))
rec = pd.DataFrame(rows)
print(rec.round(4).to_string())
summ = []
for key, g in rec.groupby(["K", "H", "unit"]):
wt = g[g.genotype == "WT"].set_index("width"); ko = g[g.genotype == "Fmr1-null"].set_index("width")
summ.append(dict(K=key[0], H=key[1], unit=key[2], WT_sig=int((wt.p < 0.05).sum()), KO_sig=int((ko.p < 0.05).sum()),
PCo_WT_gt_KO=int((wt.PCo > ko.PCo).sum())))
print("\nPer reading (of 6 timescales):")
print(pd.DataFrame(summ).to_string())
K H unit genotype width n t p PCo
0 K1 H1 R2 WT 0.025 122 0.1997 0.8420 0.9908
1 K1 H1 R2 WT 0.040 122 0.4651 0.6427 0.9869
2 K1 H1 R2 WT 0.125 122 0.7349 0.4638 0.9676
3 K1 H1 R2 WT 0.250 122 0.6790 0.4984 0.9486
4 K1 H1 R2 WT 1.000 122 1.0543 0.2938 0.8950
5 K1 H1 R2 WT 5.000 122 0.3347 0.7384 0.8028
6 K1 H1 R2 Fmr1-null 0.025 210 -1.2727 0.2045 0.8265
7 K1 H1 R2 Fmr1-null 0.040 210 -1.1861 0.2369 0.8112
8 K1 H1 R2 Fmr1-null 0.125 210 -0.5764 0.5650 0.8397
9 K1 H1 R2 Fmr1-null 0.250 210 -0.3666 0.7143 0.8308
10 K1 H1 R2 Fmr1-null 1.000 210 -0.1663 0.8681 0.8333
11 K1 H1 R2 Fmr1-null 5.000 210 0.0927 0.9263 0.7762
12 K1 H1 R1 WT 0.025 118 0.1721 0.8637 0.9908
13 K1 H1 R1 WT 0.040 118 0.4068 0.6849 0.9869
14 K1 H1 R1 WT 0.125 118 0.6963 0.4877 0.9676
15 K1 H1 R1 WT 0.250 118 0.6803 0.4977 0.9485
16 K1 H1 R1 WT 1.000 118 1.1127 0.2681 0.8947
17 K1 H1 R1 WT 5.000 118 0.4084 0.6837 0.8015
18 K1 H1 R1 Fmr1-null 0.025 152 -0.8956 0.3719 0.7534
19 K1 H1 R1 Fmr1-null 0.040 152 -0.9714 0.3329 0.7441
20 K1 H1 R1 Fmr1-null 0.125 152 -0.3855 0.7004 0.7998
21 K1 H1 R1 Fmr1-null 0.250 152 -0.2747 0.7839 0.7961
22 K1 H1 R1 Fmr1-null 1.000 152 -0.5404 0.5897 0.8133
23 K1 H1 R1 Fmr1-null 5.000 152 -0.4957 0.6208 0.7599
24 K1 H2 R2 WT 0.025 122 -2.1317 0.0351 0.9838
25 K1 H2 R2 WT 0.040 122 -2.4362 0.0163 0.9792
26 K1 H2 R2 WT 0.125 122 -1.4795 0.1416 0.9485
27 K1 H2 R2 WT 0.250 122 -1.2162 0.2263 0.9197
28 K1 H2 R2 WT 1.000 122 -0.8149 0.4167 0.8416
29 K1 H2 R2 WT 5.000 122 0.4317 0.6667 0.7206
30 K1 H2 R2 Fmr1-null 0.025 207 0.1115 0.9114 0.7922
31 K1 H2 R2 Fmr1-null 0.040 207 -0.6728 0.5018 0.8191
32 K1 H2 R2 Fmr1-null 0.125 207 0.6888 0.4917 0.8225
33 K1 H2 R2 Fmr1-null 0.250 207 0.3855 0.7003 0.8159
34 K1 H2 R2 Fmr1-null 1.000 207 1.3918 0.1655 0.7876
35 K1 H2 R2 Fmr1-null 5.000 207 -0.5097 0.6108 0.6956
36 K1 H2 R1 WT 0.025 118 -2.0365 0.0440 0.9838
37 K1 H2 R1 WT 0.040 118 -2.3539 0.0202 0.9793
38 K1 H2 R1 WT 0.125 118 -1.2222 0.2241 0.9496
39 K1 H2 R1 WT 0.250 118 -0.9371 0.3506 0.9217
40 K1 H2 R1 WT 1.000 118 -0.7570 0.4506 0.8426
41 K1 H2 R1 WT 5.000 118 0.4916 0.6239 0.7222
42 K1 H2 R1 Fmr1-null 0.025 149 0.3171 0.7517 0.6905
43 K1 H2 R1 Fmr1-null 0.040 149 -1.1348 0.2583 0.7538
44 K1 H2 R1 Fmr1-null 0.125 149 0.6353 0.5262 0.7927
45 K1 H2 R1 Fmr1-null 0.250 149 0.7069 0.4808 0.7999
46 K1 H2 R1 Fmr1-null 1.000 149 1.7489 0.0824 0.7538
47 K1 H2 R1 Fmr1-null 5.000 149 -0.0275 0.9781 0.6965
48 K2 H1 R2 WT 0.025 121 0.5386 0.5911 0.9420
49 K2 H1 R2 WT 0.040 121 -0.7763 0.4391 0.8289
50 K2 H1 R2 WT 0.125 121 0.9055 0.3670 0.8404
51 K2 H1 R2 WT 0.250 120 -0.6057 0.5459 0.7941
52 K2 H1 R2 WT 1.000 118 0.9693 0.3344 0.6861
53 K2 H1 R2 WT 5.000 96 0.6923 0.4904 0.2138
54 K2 H1 R2 Fmr1-null 0.025 204 0.1499 0.8810 0.4684
55 K2 H1 R2 Fmr1-null 0.040 204 0.6202 0.5358 0.4086
56 K2 H1 R2 Fmr1-null 0.125 203 0.4601 0.6459 0.4350
57 K2 H1 R2 Fmr1-null 0.250 199 0.2419 0.8091 0.3681
58 K2 H1 R2 Fmr1-null 1.000 193 1.1866 0.2369 0.3106
59 K2 H1 R2 Fmr1-null 5.000 162 1.2260 0.2220 0.1904
60 K2 H1 R1 WT 0.025 117 0.4116 0.6814 0.9424
61 K2 H1 R1 WT 0.040 117 -0.8241 0.4116 0.8288
62 K2 H1 R1 WT 0.125 117 0.8226 0.4124 0.8427
63 K2 H1 R1 WT 0.250 116 -0.6741 0.5016 0.7946
64 K2 H1 R1 WT 1.000 114 1.0829 0.2811 0.6871
65 K2 H1 R1 WT 5.000 92 0.4635 0.6441 0.2056
66 K2 H1 R1 Fmr1-null 0.025 146 0.6455 0.5197 0.3255
67 K2 H1 R1 Fmr1-null 0.040 146 0.6968 0.4870 0.2518
68 K2 H1 R1 Fmr1-null 0.125 145 0.5401 0.5900 0.3114
69 K2 H1 R1 Fmr1-null 0.250 141 0.2439 0.8077 0.2756
70 K2 H1 R1 Fmr1-null 1.000 136 0.8665 0.3877 0.2413
71 K2 H1 R1 Fmr1-null 5.000 108 0.8671 0.3878 0.1662
72 K2 H2 R2 WT 0.025 116 -1.1203 0.2649 0.9099
73 K2 H2 R2 WT 0.040 115 -0.6088 0.5439 0.8761
74 K2 H2 R2 WT 0.125 116 -0.5366 0.5926 0.7743
75 K2 H2 R2 WT 0.250 113 0.3256 0.7453 0.6799
76 K2 H2 R2 WT 1.000 109 -0.2097 0.8343 0.2657
77 K2 H2 R2 WT 5.000 53 0.4950 0.6227 0.4631
78 K2 H2 R2 Fmr1-null 0.025 175 -0.1732 0.8627 0.3467
79 K2 H2 R2 Fmr1-null 0.040 175 -1.1205 0.2640 0.6682
80 K2 H2 R2 Fmr1-null 0.125 174 -0.0854 0.9320 0.6358
81 K2 H2 R2 Fmr1-null 0.250 168 -0.8567 0.3928 0.5921
82 K2 H2 R2 Fmr1-null 1.000 160 1.8090 0.0723 0.4991
83 K2 H2 R2 Fmr1-null 5.000 99 -0.1583 0.8745 0.0175
84 K2 H2 R1 WT 0.025 112 -0.9093 0.3652 0.9116
85 K2 H2 R1 WT 0.040 111 -0.4024 0.6882 0.8776
86 K2 H2 R1 WT 0.125 112 -0.4566 0.6488 0.7742
87 K2 H2 R1 WT 0.250 109 0.3624 0.7177 0.6837
88 K2 H2 R1 WT 1.000 105 0.0323 0.9743 0.2794
89 K2 H2 R1 WT 5.000 50 0.2232 0.8243 0.4685
90 K2 H2 R1 Fmr1-null 0.025 119 0.0213 0.9831 0.2897
91 K2 H2 R1 Fmr1-null 0.040 119 -1.4834 0.1406 0.6884
92 K2 H2 R1 Fmr1-null 0.125 118 0.4845 0.6289 0.6750
93 K2 H2 R1 Fmr1-null 0.250 112 -0.1987 0.8429 0.6221
94 K2 H2 R1 Fmr1-null 1.000 105 2.1571 0.0333 0.5193
95 K2 H2 R1 Fmr1-null 5.000 53 -0.2237 0.8239 -0.0606
Per reading (of 6 timescales):
K H unit WT_sig KO_sig PCo_WT_gt_KO
0 K1 H1 R1 0 0 6
1 K1 H1 R2 0 0 6
2 K1 H2 R1 2 0 6
3 K1 H2 R2 2 0 6
4 K2 H1 R1 0 0 6
5 K2 H1 R2 0 0 6
6 K2 H2 R1 0 1 5
7 K2 H2 R2 0 0 5
Result: not reproduced. No reading has at least five significant Fmr1-null tests: under H1 none of the six Fmr1-null paired tests reaches p < 0.05 with K1 or K2, and under H2 at most one. WT halves do not differ under H1; under H2 with K1 the WT first two 5 min intervals differ at 25 and 40 ms (p = 0.035, 0.016). PCo is higher in WT than in Fmr1-null at all six timescales in six of the eight readings (K1, H1, R2, 25 ms: 0.991 against 0.827) and at five in the other two (K2, H2, R1 and R2), where Fmr1-null is the higher at 1 s (0.519 against 0.279, 0.499 against 0.266).
kendall-nonspatial-pyramidal¶
Kendall correlations between Fmr1-null non-spatial pyramidal cells are weaker than between WT ones at 25 ms, 40 ms, 125 ms and 1 s, but not significantly at 5 s.
Criterion: as fig4cd for non-spatial pyramidal pairs; reproduced when, under one reading, 25, 40, 125 ms and 1 s give p < 0.05 with Fmr1-null lower and 5 s gives p >= 0.05; partially reproduced when three of the four do. Expected pairs 252 and 203.
exp_ns = {0.025: (0.03, 0.006, 0.007, 0.002), 0.125: (0.04, 0.007, 0.01, 0.003)}
kn = mw_table(KT_ns, exp_ns)
print(kn.round(4).to_string())
print("\nPer reading:")
print(judge(kn, [0.025, 0.040, 0.125, 1.0], [5.0]).to_string())
print("\nBy mouse (K1, session, R2): pairs and mean tau at 25 ms")
q = KT_ns[(KT_ns.K == "K1") & (KT_ns.period == "session") & (KT_ns.width == 0.025)]
print(q.groupby(["genotype", "mouse"]).tau.agg(["count", "mean"]).round(4).to_string())
K period unit width n_WT WT WT_sem n_KO KO KO_sem U p KO_lower d WT_in_tol KO_in_tol
0 K1 session R1 0.025 205 0.0083 0.0024 115 0.0110 0.0039 9142.0 0.0009 False -0.0737 False False
1 K1 session R1 0.040 205 0.0099 0.0027 115 0.0128 0.0040 9099.0 0.0007 False -0.0740 NaN NaN
2 K1 session R1 0.125 205 0.0139 0.0035 115 0.0170 0.0048 9430.0 0.0030 False -0.0622 False False
3 K1 session R1 0.250 205 0.0164 0.0041 115 0.0203 0.0053 9649.0 0.0071 False -0.0663 NaN NaN
4 K1 session R1 1.000 205 0.0215 0.0053 115 0.0263 0.0073 10269.0 0.0559 False -0.0635 NaN NaN
5 K1 session R1 5.000 205 0.0296 0.0068 115 0.0281 0.0109 11469.0 0.6888 True 0.0146 NaN NaN
6 K1 session R2 0.025 380 0.0089 0.0016 146 0.0099 0.0032 23626.0 0.0084 False -0.0306 False False
7 K1 session R2 0.040 380 0.0109 0.0018 146 0.0118 0.0033 22994.0 0.0024 False -0.0253 NaN NaN
8 K1 session R2 0.125 380 0.0152 0.0025 146 0.0155 0.0042 23396.0 0.0054 False -0.0063 False False
9 K1 session R2 0.250 380 0.0185 0.0030 146 0.0179 0.0050 24099.0 0.0197 True 0.0104 NaN NaN
10 K1 session R2 1.000 380 0.0243 0.0041 146 0.0207 0.0086 25288.0 0.1163 True 0.0407 NaN NaN
11 K1 session R2 5.000 380 0.0276 0.0057 146 0.0219 0.0136 26797.0 0.5460 True 0.0441 NaN NaN
12 K1 first5 R1 0.025 205 0.0097 0.0027 115 0.0095 0.0043 10986.0 0.3131 True 0.0053 False False
13 K1 first5 R1 0.040 205 0.0122 0.0032 115 0.0097 0.0045 11887.0 0.9008 True 0.0555 NaN NaN
14 K1 first5 R1 0.125 205 0.0159 0.0043 115 0.0153 0.0061 11710.5 0.9233 True 0.0098 False False
15 K1 first5 R1 0.250 205 0.0186 0.0051 115 0.0167 0.0074 12087.5 0.7061 True 0.0258 NaN NaN
16 K1 first5 R1 1.000 205 0.0249 0.0070 115 0.0125 0.0104 12669.0 0.2673 True 0.1186 NaN NaN
17 K1 first5 R1 5.000 205 0.0411 0.0101 115 -0.0016 0.0168 14097.0 0.0036 True 0.2698 NaN NaN
18 K1 first5 R2 0.025 380 0.0082 0.0017 146 0.0092 0.0035 26370.0 0.3803 False -0.0294 False False
19 K1 first5 R2 0.040 380 0.0106 0.0020 146 0.0102 0.0037 28139.0 0.7985 True 0.0117 NaN NaN
20 K1 first5 R2 0.125 380 0.0148 0.0029 146 0.0150 0.0052 27580.5 0.9189 False -0.0045 False False
21 K1 first5 R2 0.250 380 0.0182 0.0034 146 0.0165 0.0062 28219.5 0.7589 True 0.0233 NaN NaN
22 K1 first5 R2 1.000 380 0.0260 0.0049 146 0.0139 0.0100 29791.0 0.1890 True 0.1190 NaN NaN
23 K1 first5 R2 5.000 380 0.0362 0.0075 146 -0.0014 0.0155 32162.0 0.0046 True 0.2365 NaN NaN
24 K2 session R1 0.025 197 0.0274 0.0072 115 0.0236 0.0070 8998.0 0.0024 True 0.0403 True False
25 K2 session R1 0.040 197 0.0314 0.0078 115 0.0321 0.0086 9212.0 0.0059 False -0.0070 NaN NaN
26 K2 session R1 0.125 193 0.0419 0.0109 115 0.0445 0.0113 9603.0 0.0481 False -0.0188 True False
27 K2 session R1 0.250 193 0.0459 0.0132 115 0.0497 0.0130 9495.0 0.0341 False -0.0225 NaN NaN
28 K2 session R1 1.000 181 0.0570 0.0186 112 0.0629 0.0154 9120.5 0.1498 False -0.0265 NaN NaN
29 K2 session R1 5.000 155 0.0293 0.0254 107 0.1200 0.0239 6794.0 0.0130 False -0.3127 NaN NaN
30 K2 session R2 0.025 369 0.0251 0.0045 146 0.0214 0.0057 22680.0 0.0052 True 0.0449 True False
31 K2 session R2 0.040 368 0.0297 0.0051 146 0.0299 0.0071 22340.0 0.0029 False -0.0021 NaN NaN
32 K2 session R2 0.125 364 0.0392 0.0071 146 0.0411 0.0095 23155.5 0.0232 False -0.0143 True False
33 K2 session R2 0.250 364 0.0443 0.0086 146 0.0431 0.0113 24037.5 0.0921 True 0.0074 NaN NaN
34 K2 session R2 1.000 345 0.0638 0.0124 143 0.0505 0.0143 23616.5 0.4588 True 0.0618 NaN NaN
35 K2 session R2 5.000 310 0.0383 0.0172 138 0.1068 0.0230 18214.5 0.0121 False -0.2334 NaN NaN
36 K2 first5 R1 0.025 159 0.0316 0.0088 98 0.0103 0.0048 7397.0 0.4966 True 0.2311 True False
37 K2 first5 R1 0.040 156 0.0362 0.0093 99 0.0124 0.0054 7692.0 0.9590 True 0.2465 NaN NaN
38 K2 first5 R1 0.125 151 0.0432 0.0150 95 0.0272 0.0121 6819.0 0.5159 True 0.0984 True False
39 K2 first5 R1 0.250 155 0.0620 0.0188 95 0.0396 0.0182 7369.5 0.9907 True 0.1045 NaN NaN
40 K2 first5 R1 1.000 136 0.0746 0.0277 86 0.0457 0.0282 5907.0 0.9001 True 0.0959 NaN NaN
41 K2 first5 R1 5.000 76 0.0866 0.0553 68 0.0925 0.0490 2610.5 0.9171 False -0.0130 NaN NaN
42 K2 first5 R2 0.025 304 0.0253 0.0056 127 0.0155 0.0055 18140.0 0.3237 True 0.1114 True False
43 K2 first5 R2 0.040 302 0.0288 0.0060 128 0.0244 0.0084 18953.0 0.7506 True 0.0440 NaN NaN
44 K2 first5 R2 0.125 290 0.0382 0.0094 123 0.0299 0.0112 17248.0 0.5970 True 0.0554 True False
45 K2 first5 R2 0.250 285 0.0492 0.0123 123 0.0457 0.0176 17063.0 0.6712 True 0.0171 NaN NaN
46 K2 first5 R2 1.000 240 0.0754 0.0189 115 0.0662 0.0257 13701.0 0.9133 True 0.0320 NaN NaN
47 K2 first5 R2 5.000 166 0.0198 0.0363 89 0.0629 0.0440 7053.5 0.5530 False -0.0957 NaN NaN
Per reading:
K period unit short_sig long_ns n_WT n_KO
0 K1 first5 R1 0 0 205 115
1 K1 first5 R2 0 0 380 146
2 K1 session R1 0 1 205 115
3 K1 session R2 0 1 380 146
4 K2 first5 R1 0 1 159 98
5 K2 first5 R2 0 1 304 127
6 K2 session R1 1 0 197 115
7 K2 session R2 1 0 369 146
By mouse (K1, session, R2): pairs and mean tau at 25 ms
count mean
genotype mouse
Fmr1-null M10 1 0.0003
M18 35 0.0037
M29 1 0.0061
M33 21 0.0249
M34 32 0.0071
M37 53 0.0105
M9 3 -0.0000
WT M11 1 -0.0023
M16 297 0.0089
M28 10 0.0110
M32 35 0.0142
M35 22 0.0051
M36 15 0.0018
Result: not reproduced. There are 380 WT and 146 Fmr1-null non-spatial pairs in R2 (expected 252 and 203). With K1 over the session the distributions differ at 25, 40 and 125 ms (p = 0.0084, 0.0024, 0.0054) but Fmr1-null mean tau is not lower (0.0099 against 0.0089 at 25 ms), and 1 s does not differ (p = 0.12). No reading gives Fmr1-null lower at p < 0.05 on more than one of the four timescales (K2 over the session, 25 ms, p = 0.0024 in R1).
Refractory filter and 32 x 32 grid for the pass and Kendall claims¶
The two variants of the cell table (refractory filter, 32 x 32 grid; see the cell table section) applied to the claims that use passes or Kendall correlations, R2 only. With the refractory filter a pass or a pair enters only when its cells pass the filter. On the 32 x 32 grid the place cells are those classified on the grid, and exp is computed from their 32 x 32 rate maps. The covariance shuffle uses its own generator. fig4d-network-state-recurrence, kendall-quiet-states and the rotating arena claims are not run under these variants.
T_V = time.time()
keep_iso = set(zip(R2_iso.session, R2_iso.cell))
def kept(d, *cols):
k = np.ones(len(d), bool)
for col in cols:
k &= np.array([(s, c) in keep_iso for s, c in zip(d.session, d[col])])
return d[k]
def zframe(P):
return pd.DataFrame([dict(session=k[0], cell=k[1], genotype=S[k[0]].genotype, z=zz)
for k, v in P.items() for zz in v["z"][v["is_pass"]]])
P_code = pass_table(R2_code, index_fn=hc16.pixel_index_code)
P_code_count = pass_table(R2_code, rule="count", index_fn=hc16.pixel_index_code)
variants = {"literal": {"as above": pz, "refractory filter": kept(pz, "cell"), "32 x 32 grid": zframe(P_code)},
"count": {"as above": pz_count, "refractory filter": kept(pz_count, "cell"), "32 x 32 grid": zframe(P_code_count)}}
print("R2 place cells with passes:", {f"{rule}, {name}": zz.groupby("genotype")[["session", "cell"]].apply(lambda d: len(d.drop_duplicates())).to_dict()
for rule, vv in variants.items() for name, zz in vv.items()})
print("\noverdispersion-passes (R2), variance of z over passes:")
print(pd.DataFrame([od_passes(zz, f"{rule} rule, {name}") for rule, vv in variants.items() for name, zz in vv.items()]).round(4).to_string())
print("\noverdispersion-cells (R2), mean of the per-cell variance of z, one-tailed WT > Fmr1-null:")
rows = []
for rule, vv in variants.items():
for name, zz in vv.items():
pv = zz.groupby(["session", "cell", "genotype"]).z.var().reset_index()
r = hc16.two_group(pv[pv.genotype == "WT"].z, pv[pv.genotype == "Fmr1-null"].z, alternative="greater")
rows.append(dict(reading=f"{rule} rule, {name}", n_WT=r["n_a"], WT=r["mean_a"], WT_sem=r["sem_a"], n_KO=r["n_b"], KO=r["mean_b"],
KO_sem=r["sem_b"], p_t_one=r["p_t"], p_U_one=r["p_U"],
WT_in_tol=abs(r["mean_a"] - 5.98) <= 0.44, KO_in_tol=abs(r["mean_b"] - 5.02) <= 0.3))
print(pd.DataFrame(rows).round(4).to_string())
print("\nfig4b-covariance-30-passes and -40-passes (R2), pair r over joint passes:")
rng_var = np.random.default_rng(406)
covs = {"literal": {"as above": cov, "refractory filter": kept(cov, "a", "b"), "32 x 32 grid": pair_cov(P_code, rng=rng_var)},
"count": {"as above": cov_count, "refractory filter": kept(cov_count, "a", "b"), "32 x 32 grid": pair_cov(P_code_count, rng=rng_var)}}
rows = []
for rule, vv in covs.items():
for name, d0 in vv.items():
for mn, exp in [(30, (0.09, 0.03, 0.04, 0.01)), (40, (0.19, 0.04, 0.06, 0.01))]:
d = d0[(d0.n >= mn) & d0.r.notna()]
a = d[d.genotype == "WT"]; b = d[d.genotype == "Fmr1-null"]
t = stats.ttest_ind(hc16.fisher(a.r), hc16.fisher(b.r))
rows.append(dict(reading=f"{rule} rule, {name}", min_passes=mn, joint_min=int(d0.n.min()), n_WT=len(a), r_WT=a.r.mean(),
n_KO=len(b), r_KO=b.r.mean(), t=t.statistic, p_t=t.pvalue,
shuf1_p=stats.ttest_ind(hc16.fisher(a.r_shuf1), hc16.fisher(b.r_shuf1)).pvalue,
shuf100_p=stats.ttest_ind(a.r_shuf100, b.r_shuf100).pvalue,
WT_in_tol=abs(a.r.mean() - exp[0]) <= exp[1], KO_in_tol=abs(b.r.mean() - exp[2]) <= exp[3]))
print(pd.DataFrame(rows).round(4).to_string())
KT_place_code = pd.concat([session_taus(R2_code, "place", False), session_taus(R2_code, "place", True)], ignore_index=True)
KT_ns_code = pd.concat([session_taus(R2_code, "nonspatial", False), session_taus(R2_code, "nonspatial", True)], ignore_index=True)
for claim, kts, short, long_ in [("fig4cd-kendall-place-cells", {"refractory filter": kept(KT_place, "a", "b"), "32 x 32 grid": KT_place_code},
[0.025, 0.040, 0.125], [1.0, 5.0]),
("kendall-nonspatial-pyramidal", {"refractory filter": kept(KT_ns, "a", "b"), "32 x 32 grid": KT_ns_code},
[0.025, 0.040, 0.125, 1.0], [5.0])]:
for name, kt in kts.items():
m = mw_table(kt)
print(f"\n{claim}, {name}: per reading, timescales {short} with p < 0.05 and Fmr1-null lower, and {long_} with p >= 0.05")
print(judge(m, short, long_).to_string())
print(" K1, session, R2:", "; ".join(f"{w * 1000:g} ms: WT {r.WT:.4f}, KO {r.KO:.4f}, p = {r.p:.3g}"
for w, r in m[(m.K == "K1") & (m.period == "session") & (m.unit == "R2")].set_index("width").iterrows()))
print(f"\nVariants computed in {time.time() - T_V:.0f} s")
R2 place cells with passes: {'literal, as above': {'Fmr1-null': 70, 'WT': 55}, 'literal, refractory filter': {'Fmr1-null': 70, 'WT': 54}, 'literal, 32 x 32 grid': {'Fmr1-null': 81, 'WT': 65}, 'count, as above': {'Fmr1-null': 70, 'WT': 55}, 'count, refractory filter': {'Fmr1-null': 70, 'WT': 54}, 'count, 32 x 32 grid': {'Fmr1-null': 81, 'WT': 65}}
overdispersion-passes (R2), variance of z over passes:
reading n_WT var_WT n_KO var_KO F p_F_one_sided p_F_two_sided levene_F levene_p WT_in_tol KO_in_tol passes_in_tol
0 literal rule, as above 12802 3.6903 18521 3.5493 1.0397 0.0082 0.0164 0.0000 0.9977 False False False
1 literal rule, refractory filter 12652 3.7075 18521 3.5493 1.0446 0.0037 0.0074 0.0298 0.8630 False False False
2 literal rule, 32 x 32 grid 15777 3.7305 22036 3.7297 1.0002 0.4940 0.9880 4.7290 0.0297 False False False
3 count rule, as above 5442 5.2789 8250 4.6635 1.1319 0.0000 0.0000 11.6845 0.0006 True True False
4 count rule, refractory filter 5386 5.3018 8250 4.6635 1.1369 0.0000 0.0000 12.5852 0.0004 True True False
5 count rule, 32 x 32 grid 6712 5.3367 9607 4.8920 1.0909 0.0001 0.0001 3.7825 0.0518 True True True
overdispersion-cells (R2), mean of the per-cell variance of z, one-tailed WT > Fmr1-null:
reading n_WT WT WT_sem n_KO KO KO_sem p_t_one p_U_one WT_in_tol KO_in_tol
0 literal rule, as above 55 3.6828 0.2409 70 3.5280 0.2134 0.3157 0.2728 False False
1 literal rule, refractory filter 54 3.7093 0.2440 70 3.5280 0.2134 0.2884 0.2426 False False
2 literal rule, 32 x 32 grid 65 3.8410 0.2590 81 3.7695 0.2093 0.4142 0.4937 False False
3 count rule, as above 55 5.3285 0.4593 70 4.6888 0.3268 0.1231 0.1605 False False
4 count rule, refractory filter 54 5.3699 0.4660 70 4.6888 0.3268 0.1102 0.1421 False False
5 count rule, 32 x 32 grid 65 5.5730 0.4715 81 5.0501 0.3279 0.1755 0.2840 True True
fig4b-covariance-30-passes and -40-passes (R2), pair r over joint passes:
reading min_passes joint_min n_WT r_WT n_KO r_KO t p_t shuf1_p shuf100_p WT_in_tol KO_in_tol 0 literal rule, as above 30 39 122 0.0608 210 0.0210 2.2890 0.0227 0.2130 0.2193 True False 1 literal rule, as above 40 39 122 0.0608 209 0.0200 2.3264 0.0206 0.2279 0.2539 False False 2 literal rule, refractory filter 30 39 114 0.0606 210 0.0210 2.2777 0.0234 0.2825 0.0884 True False 3 literal rule, refractory filter 40 39 114 0.0606 209 0.0200 2.3141 0.0213 0.3003 0.1051 False False 4 literal rule, 32 x 32 grid 30 41 183 0.0744 290 0.0233 3.3426 0.0009 0.8379 0.0204 True False 5 literal rule, 32 x 32 grid 40 41 183 0.0744 290 0.0233 3.3426 0.0009 0.8379 0.0204 False False 6 count rule, as above 30 3 50 0.1646 103 0.0074 3.5426 0.0005 0.0004 0.5060 False False 7 count rule, as above 40 3 30 0.2786 86 0.0394 4.4072 0.0000 0.0639 0.9557 False False 8 count rule, refractory filter 30 3 49 0.1658 103 0.0074 3.5488 0.0005 0.0007 0.5154 False False 9 count rule, refractory filter 40 3 29 0.2846 86 0.0394 4.4669 0.0000 0.1040 0.9423 False False 10 count rule, 32 x 32 grid 30 3 94 0.1221 151 0.0264 3.2748 0.0012 0.5985 0.1294 False False 11 count rule, 32 x 32 grid 40 3 65 0.1819 121 0.0556 3.6077 0.0004 0.5340 0.4207 True True
fig4cd-kendall-place-cells, refractory filter: per reading, timescales [0.025, 0.04, 0.125] with p < 0.05 and Fmr1-null lower, and [1.0, 5.0] with p >= 0.05
K period unit short_sig long_ns n_WT n_KO
0 K1 first5 R1 0 0 110 152
1 K1 first5 R2 0 1 114 210
2 K1 session R1 0 0 110 152
3 K1 session R2 0 0 114 210
4 K2 first5 R1 0 2 106 135
5 K2 first5 R2 0 2 110 191
6 K2 session R1 0 2 110 149
7 K2 session R2 0 2 114 207
K1, session, R2: 25 ms: WT 0.0308, KO 0.0067, p = 0.845; 40 ms: WT 0.0336, KO 0.0084, p = 0.622; 125 ms: WT 0.0385, KO 0.0082, p = 0.668; 250 ms: WT 0.0416, KO 0.0079, p = 0.429; 1000 ms: WT 0.0525, KO 0.0059, p = 0.0425; 5000 ms: WT 0.0801, KO 0.0032, p = 0.00094
fig4cd-kendall-place-cells, 32 x 32 grid: per reading, timescales [0.025, 0.04, 0.125] with p < 0.05 and Fmr1-null lower, and [1.0, 5.0] with p >= 0.05
K period unit short_sig long_ns n_WT n_KO
0 K1 first5 R1 0 0 170 246
1 K1 first5 R2 0 0 183 290
2 K1 session R1 0 0 170 246
3 K1 session R2 0 0 183 290
4 K2 first5 R1 0 2 162 223
5 K2 first5 R2 0 2 175 265
6 K2 session R1 0 2 170 243
7 K2 session R2 0 1 183 287
K1, session, R2: 25 ms: WT 0.0253, KO 0.0074, p = 0.948; 40 ms: WT 0.0288, KO 0.0096, p = 0.963; 125 ms: WT 0.0346, KO 0.0108, p = 0.281; 250 ms: WT 0.0380, KO 0.0119, p = 0.242; 1000 ms: WT 0.0514, KO 0.0130, p = 0.0215; 5000 ms: WT 0.0740, KO 0.0124, p = 0.00105
kendall-nonspatial-pyramidal, refractory filter: per reading, timescales [0.025, 0.04, 0.125, 1.0] with p < 0.05 and Fmr1-null lower, and [5.0] with p >= 0.05
K period unit short_sig long_ns n_WT n_KO
0 K1 first5 R1 0 0 192 111
1 K1 first5 R2 0 0 361 132
2 K1 session R1 0 1 192 111
3 K1 session R2 1 1 361 132
4 K2 first5 R1 0 1 147 94
5 K2 first5 R2 0 1 287 114
6 K2 session R1 2 0 184 111
7 K2 session R2 2 0 350 132
K1, session, R2: 25 ms: WT 0.0092, KO 0.0101, p = 0.00902; 40 ms: WT 0.0113, KO 0.0119, p = 0.00248; 125 ms: WT 0.0157, KO 0.0152, p = 0.0112; 250 ms: WT 0.0190, KO 0.0173, p = 0.039; 1000 ms: WT 0.0242, KO 0.0196, p = 0.164; 5000 ms: WT 0.0264, KO 0.0190, p = 0.645
kendall-nonspatial-pyramidal, 32 x 32 grid: per reading, timescales [0.025, 0.04, 0.125, 1.0] with p < 0.05 and Fmr1-null lower, and [5.0] with p >= 0.05
K period unit short_sig long_ns n_WT n_KO
0 K1 first5 R1 0 1 176 72
1 K1 first5 R2 0 1 327 103
2 K1 session R1 0 1 176 72
3 K1 session R2 0 1 327 103
4 K2 first5 R1 0 1 131 57
5 K2 first5 R2 0 1 253 86
6 K2 session R1 0 0 168 72
7 K2 session R2 0 0 316 103
K1, session, R2: 25 ms: WT 0.0083, KO 0.0110, p = 0.00378; 40 ms: WT 0.0104, KO 0.0136, p = 0.000474; 125 ms: WT 0.0150, KO 0.0193, p = 0.00333; 250 ms: WT 0.0186, KO 0.0235, p = 0.00384; 1000 ms: WT 0.0242, KO 0.0301, p = 0.0178; 5000 ms: WT 0.0274, KO 0.0333, p = 0.245
Variants computed in 44 s
Under the claims file's literal pass rule neither variant changes these results. overdispersion-passes: with the refractory filter the variances are 3.71 and 3.55 (Levene p = 0.86); on the 32 x 32 grid 3.73 and 3.73 (F one-sided p = 0.49); none is within tolerance. overdispersion-cells: one-tailed p = 0.29 (filter) and 0.41 (grid), no mean within tolerance. fig4b covariance: with the filter r = 0.061 against 0.021, p = 0.023; on the grid 183 WT and 290 Fmr1-null pairs, r = 0.074 against 0.023, p = 0.0009, where the fewest joint passes is 41, so the 30 and 40 pass minimums select the same pairs; the Fmr1-null average is outside tolerance in every case, and on the grid the mean of 100 permutations still differs (p = 0.020) where one permutation per pair does not (p = 0.84). fig4cd Kendall place cells: under either variant no reading has a 25, 40 or 125 ms comparison with p < 0.05 and Fmr1-null lower (K1, session: 1 s and 5 s p = 0.043 and 0.0009 with the filter, 0.022 and 0.0011 on the grid). Kendall non-spatial pairs: at most two of the four timescales with Fmr1-null lower at p < 0.05 (K2 over the session, filter), none on the grid, where Fmr1-null tau is the higher (K1, session, 25 ms, 0.0110 against 0.0083).
Under the count rule, a variant asked of a reader: overdispersion-passes with the refractory filter has both variances within tolerance (5.30, 4.66; F one-sided p < 0.0001, Levene p = 0.0004), as without it. On the grid the variances (5.34, 4.89) and the pass counts (6,712, 9,607) are within tolerance and the F test gives p = 0.0001, but Levene's test p = 0.052. overdispersion-cells on the grid has both means within tolerance (5.57, 5.05) with one-tailed p = 0.18. fig4b-covariance-40-passes on the grid has 65 WT and 121 Fmr1-null pairs, r = 0.182 against 0.056, both within tolerance, t p = 0.0004, which meets its criterion; at 30 passes the Fmr1-null average (0.026) is outside tolerance.
kendall-quiet-states¶
During quiet wakefulness and sleep, place cell pair correlations do not differ at the gamma timescales (25, 40 ms) but are weaker in Fmr1-null at 125 ms, 250 ms and 1 s.
Criterion: tau (K1; K2 a variant) for place cell pairs over quiet periods; the readings are the recordings the data label as sleep or rest (there are none in the data set) and periods of speed below 2 cm/s in the session. Reproduced when 25 and 40 ms give p >= 0.05 and 125 ms, 250 ms and 1 s give p < 0.05 with Fmr1-null lower. Speed from room-frame positions smoothed over 1 s.
SPEED = {s.id: hc16.speed(s) for s in sessions}
def slow_mask(s, sample, edges):
"""Bins in which the speed is below 2 cm/s at every position sample and at the bin centre."""
v = np.nan_to_num(SPEED[s.id], nan=np.inf)
lo = np.searchsorted(s.sample_time, edges[:-1]); hi = np.searchsorted(s.sample_time, edges[1:])
vmax = np.where(hi > lo, np.maximum.reduceat(v, np.clip(lo, 0, len(v) - 1)), -np.inf)
return np.maximum(vmax, v[sample]) < 2.0
def slow_mask_centre(s, sample, edges):
return np.nan_to_num(SPEED[s.id][sample], nan=np.inf) < 2.0
out = []
for mname, mfn in [("whole bin", slow_mask), ("centre", slow_mask_centre)]:
for gate in [False, True]:
for sid, g in R2[R2.cls == "place"].groupby("session"):
cl = list(g.cell)
if len(cl) >= 2:
d = hc16.pair_taus(S[sid], cl, gate=gate, mask_fn=mfn)
d["K"] = ("K2" if gate else "K1") + ("" if mname == "whole bin" else ", centre")
out.append(d)
KQ = pd.concat(out, ignore_index=True)
KQ["in_R1"] = KQ.session.isin(set(R1.session))
slow_frac = pd.Series({sid: np.mean(np.nan_to_num(SPEED[sid], nan=np.inf) < 2) for sid in R2.session.unique()})
print("Fraction of samples below 2 cm/s per session: median %.3f, range %.3f to %.3f" % (slow_frac.median(), slow_frac.min(), slow_frac.max()))
print("Bins entering (K1, median by width), whole bin:", KQ[KQ.K == "K1"].groupby("width").nbins.median().to_dict())
print("Bins entering (K1, median by width), centre only:", KQ[KQ.K == "K1, centre"].groupby("width").nbins.median().to_dict())
rows = []
for K in ["K1", "K2", "K1, centre", "K2, centre"]:
for unit in ["R2", "R1"]:
q = KQ[KQ.K == K]
if unit == "R1":
q = q[q.in_R1]
for w in hc16.TIMESCALES:
qq = q[q.width == w].dropna(subset=["tau"])
a = qq[qq.genotype == "WT"].tau; b = qq[qq.genotype == "Fmr1-null"].tau
u = stats.mannwhitneyu(a, b)
rows.append(dict(K=K, unit=unit, width=w, n_WT=len(a), WT=a.mean(), n_KO=len(b), KO=b.mean(), U=u.statistic,
p=u.pvalue, KO_lower=b.mean() < a.mean()))
kq = pd.DataFrame(rows)
print(kq.round(4).to_string())
for key, g in kq.groupby(["K", "unit"]):
g = g.set_index("width")
print(key, "25/40 ms p >= 0.05:", int((g.loc[[0.025, 0.040], "p"] >= 0.05).sum()), "of 2;",
"125/250 ms/1 s p < 0.05 and Fmr1-null lower:", int(((g.loc[[0.125, 0.25, 1.0], "p"] < 0.05) & g.loc[[0.125, 0.25, 1.0], "KO_lower"]).sum()), "of 3")
Fraction of samples below 2 cm/s per session: median 0.212, range 0.127 to 0.419
Bins entering (K1, median by width), whole bin: {0.025: 9857.0, 0.04: 6053.0, 0.125: 1613.0, 0.25: 630.0, 1.0: 64.0, 5.0: 2.0}
Bins entering (K1, median by width), centre only: {0.025: 10179.0, 0.04: 6353.0, 0.125: 2033.0, 0.25: 1023.0, 1.0: 256.0, 5.0: 56.0}
K unit width n_WT WT n_KO KO U p KO_lower
0 K1 R2 0.025 122 0.0304 210 0.0138 12570.0 0.7764 True
1 K1 R2 0.040 122 0.0328 210 0.0185 12178.0 0.4539 True
2 K1 R2 0.125 122 0.0397 210 0.0249 12112.0 0.4081 True
3 K1 R2 0.250 122 0.0475 210 0.0303 12318.0 0.5600 True
4 K1 R2 1.000 122 0.0554 210 0.0238 13374.0 0.5039 True
5 K1 R2 5.000 50 0.2269 45 0.0925 1331.5 0.1245 True
6 K1 R1 0.025 118 0.0315 152 0.0096 9381.0 0.5169 True
7 K1 R1 0.040 118 0.0340 152 0.0133 9130.0 0.7997 True
8 K1 R1 0.125 118 0.0408 152 0.0184 8950.0 0.9781 True
9 K1 R1 0.250 118 0.0491 152 0.0218 9217.0 0.6962 True
10 K1 R1 1.000 118 0.0574 152 0.0119 9746.0 0.2218 True
11 K1 R1 5.000 50 0.2269 42 0.1106 1227.5 0.1650 True
12 K2 R2 0.025 114 0.0327 203 0.0161 11140.0 0.5825 True
13 K2 R2 0.040 115 0.0335 203 0.0234 10113.0 0.0478 True
14 K2 R2 0.125 113 0.0045 199 0.0198 9505.0 0.0232 False
15 K2 R2 0.250 103 0.0317 196 0.0200 9461.0 0.3733 True
16 K2 R2 1.000 58 0.0839 105 0.0766 3147.5 0.7237 True
17 K2 R2 5.000 0 NaN 1 0.3333 NaN NaN False
18 K2 R1 0.025 110 0.0341 146 0.0106 8178.0 0.8014 True
19 K2 R1 0.040 111 0.0351 146 0.0160 7460.0 0.2764 True
20 K2 R1 0.125 109 0.0041 142 0.0102 6900.0 0.1414 False
21 K2 R1 0.250 99 0.0343 139 0.0079 6833.0 0.9285 True
22 K2 R1 1.000 55 0.0955 61 0.1032 1724.0 0.7992 False
23 K2 R1 5.000 0 NaN 1 0.3333 NaN NaN False
24 K1, centre R2 0.025 122 0.0303 210 0.0135 12554.0 0.7619 True
25 K1, centre R2 0.040 122 0.0329 210 0.0179 12200.0 0.4698 True
26 K1, centre R2 0.125 122 0.0379 210 0.0229 12079.0 0.3863 True
27 K1, centre R2 0.250 122 0.0433 210 0.0236 12724.0 0.9192 True
28 K1, centre R2 1.000 122 0.0487 210 0.0247 13049.0 0.7773 True
29 K1, centre R2 5.000 122 0.0865 210 0.0085 15353.0 0.0026 True
30 K1, centre R1 0.025 118 0.0314 152 0.0092 9364.0 0.5343 True
31 K1, centre R1 0.040 118 0.0342 152 0.0129 9105.0 0.8302 True
32 K1, centre R1 0.125 118 0.0392 152 0.0167 8966.0 0.9981 True
33 K1, centre R1 0.250 118 0.0448 152 0.0166 9453.0 0.4465 True
34 K1, centre R1 1.000 118 0.0506 152 0.0122 9670.0 0.2704 True
35 K1, centre R1 5.000 118 0.0903 152 0.0087 10866.0 0.0029 True
36 K2, centre R2 0.025 114 0.0329 203 0.0160 11125.0 0.5694 True
37 K2, centre R2 0.040 115 0.0339 203 0.0232 10288.0 0.0789 True
38 K2, centre R2 0.125 117 0.0157 201 0.0183 9935.0 0.0211 False
39 K2, centre R2 0.250 115 0.0184 204 0.0059 10875.0 0.2800 True
40 K2, centre R2 1.000 107 -0.0154 193 0.0051 10050.0 0.7024 False
41 K2, centre R2 5.000 62 0.1394 135 0.0164 4733.5 0.1403 True
42 K2, centre R1 0.025 110 0.0344 146 0.0105 8156.0 0.8306 True
43 K2, centre R1 0.040 111 0.0354 146 0.0161 7541.0 0.3415 True
44 K2, centre R1 0.125 113 0.0162 144 0.0093 7238.0 0.1292 True
45 K2, centre R1 0.250 111 0.0184 146 -0.0096 7915.0 0.7508 True
46 K2, centre R1 1.000 103 -0.0171 136 -0.0063 6993.0 0.9842 False
47 K2, centre R1 5.000 59 0.1453 88 0.0133 2977.0 0.1326 True
('K1', 'R1') 25/40 ms p >= 0.05: 2 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
('K1', 'R2') 25/40 ms p >= 0.05: 2 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
('K1, centre', 'R1') 25/40 ms p >= 0.05: 2 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
('K1, centre', 'R2') 25/40 ms p >= 0.05: 2 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
('K2', 'R1') 25/40 ms p >= 0.05: 2 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
('K2', 'R2') 25/40 ms p >= 0.05: 1 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
('K2, centre', 'R1') 25/40 ms p >= 0.05: 2 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
('K2, centre', 'R2') 25/40 ms p >= 0.05: 2 of 2; 125/250 ms/1 s p < 0.05 and Fmr1-null lower: 0 of 3
/tmp/ipykernel_2508/112282393.py:34: SmallSampleWarning: One or more sample arguments is too small; all returned values will be NaN. See documentation for sample size requirements. u = stats.mannwhitneyu(a, b) /tmp/ipykernel_2508/112282393.py:34: SmallSampleWarning: One or more sample arguments is too small; all returned values will be NaN. See documentation for sample size requirements. u = stats.mannwhitneyu(a, b)
Result: not reproduced. No recording is labelled as sleep or rest, so only the low-speed reading runs. Bins enter when the speed is below 2 cm/s at every position sample in them (21% of a session's samples are below 2 cm/s, median). 25 and 40 ms do not differ (K1, R2: p = 0.78, 0.45), as claimed, but neither do 125 ms, 250 ms and 1 s (p = 0.41, 0.56, 0.50); 5 s keeps a few bins in few pairs (50 WT, 45 Fmr1-null pairs; p = 0.12). Keeping a bin when the speed at its centre is low gives the same pattern (p = 0.39, 0.92, 0.78 at 125 ms, 250 ms and 1 s). With K2 (R2), 40 ms differs (p = 0.048) and 125 ms differs with Fmr1-null higher in mean tau (p = 0.023).
locomotion-similar¶
WT and Fmr1-null mice do not differ in locomotion speed or path linearity during the recordings, and the change in speed between the two halves of a session does not differ between genotypes.
Criterion: a two-way genotype x half ANOVA with repeated measures on half, on average speed (Figure S5B inset); reproduced when the genotype effect and the interaction have p >= 0.05. Recordings and mice as units, both run. Recordings: the stationary sessions used for the Kendall claims. Linearity is not judged.
import pingouin as pg
rows = []
for s in sessions:
if s.env == "rotating_cylinder":
continue
v = SPEED[s.id]; h = s.nsamp // 2
rows += [dict(session=s.id, mouse=s.mouse, genotype=s.genotype, env=s.env, half="first", speed=np.nanmean(v[:h])),
dict(session=s.id, mouse=s.mouse, genotype=s.genotype, env=s.env, half="second", speed=np.nanmean(v[h:]))]
loc = pd.DataFrame(rows)
print("Speed (cm/s) by genotype and half, recordings:")
print(loc.groupby(["genotype", "half"]).speed.agg(["count", "mean", "sem"]).round(3).to_string())
print("Range %.2f to %.2f cm/s" % (loc.speed.min(), loc.speed.max()))
a1 = pg.mixed_anova(data=loc, dv="speed", within="half", between="genotype", subject="session")
print("\nRecordings as units:\n", a1.round(4).to_string())
lm = loc.groupby(["mouse", "genotype", "half"]).speed.mean().reset_index()
a2 = pg.mixed_anova(data=lm, dv="speed", within="half", between="genotype", subject="mouse")
print("\nMice as units:\n", a2.round(4).to_string())
print("\nBy mouse:")
print(lm.pivot_table(index=["genotype", "mouse"], columns="half", values="speed").round(2).to_string())
for env in ["big_box", "stationary_cylinder"]:
a3 = pg.mixed_anova(data=loc[loc.env == env], dv="speed", within="half", between="genotype", subject="session")
print(f"\nVariant, {env} recordings only:\n", a3[["Source", "F", "DF1", "DF2", [c for c in a3.columns if c.startswith("p")][0]]].round(4).to_string())
print("\nSpeed (cm/s) by environment, genotype and half, recordings; change = second - first:")
sp = loc.groupby(["env", "genotype", "half"]).speed.mean().unstack()
print(sp.assign(change=sp["second"] - sp["first"]).round(2).to_string())
for env in ["big_box", "stationary_cylinder"]:
le = loc[loc.env == env].groupby(["mouse", "genotype", "half"]).speed.mean().reset_index()
a4 = pg.mixed_anova(data=le, dv="speed", within="half", between="genotype", subject="mouse")
print(f"\nVariant, {env}, mice as units ({le.mouse.nunique()} mice):\n", a4[["Source", "F", "DF1", "DF2", [c for c in a4.columns if c.startswith("p")][0]]].round(4).to_string())
lr1 = loc[loc.session.isin(set(R1.session))]
a5 = pg.mixed_anova(data=lr1, dv="speed", within="half", between="genotype", subject="session")
print(f"\nVariant, one recording per mouse and day (R1, {lr1.session.nunique()} recordings):\n", a5[["Source", "F", "DF1", "DF2", [c for c in a5.columns if c.startswith("p")][0]]].round(4).to_string())
print("\nUnits each set of recordings gives (the paper's 27 error df need 29):",
{"stationary recordings": loc.session.nunique(), "one per mouse and day": lr1.session.nunique(),
"large box": loc[loc.env == "big_box"].session.nunique(), "stationary arena": loc[loc.env == "stationary_cylinder"].session.nunique(),
"with rotating sessions": loc.session.nunique() + len([s for s in sessions if s.env == "rotating_cylinder"]),
"mice": loc.mouse.nunique()})
Speed (cm/s) by genotype and half, recordings:
count mean sem
genotype half
Fmr1-null first 17 6.306 0.391
second 17 5.657 0.351
WT first 17 6.634 0.447
second 17 5.858 0.396
Range 2.84 to 11.55 cm/s
Recordings as units:
Source SS DF1 DF2 MS F p_unc np2 eps
0 genotype 1.1928 1 32 1.1928 0.2389 0.6283 0.0074 NaN
1 half 8.6252 1 32 8.6252 22.7869 0.0000 0.4159 1.0
2 Interaction 0.0686 1 32 0.0686 0.1813 0.6731 0.0056 1.0
Mice as units:
Source SS DF1 DF2 MS F p_unc np2 eps
0 genotype 3.1709 1 13 3.1709 0.6311 0.4412 0.0463 NaN
1 half 3.9371 1 13 3.9371 10.8646 0.0058 0.4553 1.0
2 Interaction 0.3873 1 13 0.3873 1.0689 0.3200 0.0760 1.0
By mouse:
half first second
genotype mouse
Fmr1-null M10 3.63 4.54
M18 6.58 5.69
M29 7.41 7.11
M33 5.04 4.10
M34 6.88 6.41
M37 6.39 5.16
M9 6.66 6.21
WT M1 5.45 5.63
M11 11.55 9.30
M16 7.03 5.98
M28 5.02 4.52
M3 6.14 3.64
M32 6.30 5.79
M35 6.15 5.44
M36 8.07 7.92
Variant, big_box recordings only:
Source F DF1 DF2 p_unc
0 genotype 2.0364 1 13 0.1771
1 half 11.5709 1 13 0.0047
2 Interaction 6.8945 1 13 0.0210
Variant, stationary_cylinder recordings only:
Source F DF1 DF2 p_unc
0 genotype 0.7473 1 17 0.3994
1 half 26.5423 1 17 0.0001
2 Interaction 9.1504 1 17 0.0076
Speed (cm/s) by environment, genotype and half, recordings; change = second - first:
half first second change
env genotype
big_box Fmr1-null 5.23 5.10 -0.14
WT 7.17 5.80 -1.37
stationary_cylinder Fmr1-null 7.06 6.05 -1.01
WT 6.15 5.91 -0.25
Variant, big_box, mice as units (15 mice):
Source F DF1 DF2 p_unc
0 genotype 2.0364 1 13 0.1771
1 half 11.5709 1 13 0.0047
2 Interaction 6.8945 1 13 0.0210
Variant, stationary_cylinder, mice as units (10 mice):
Source F DF1 DF2 p_unc
0 genotype 0.1661 1 8 0.6943
1 half 12.1696 1 8 0.0082
2 Interaction 7.6664 1 8 0.0243
Variant, one recording per mouse and day (R1, 25 recordings):
Source F DF1 DF2 p_unc
0 genotype 0.7002 1 23 0.4113
1 half 12.6757 1 23 0.0017
2 Interaction 1.7809 1 23 0.1951
Units each set of recordings gives (the paper's 27 error df need 29): {'stationary recordings': 34, 'one per mouse and day': 25, 'large box': 15, 'stationary arena': 19, 'with rotating sessions': 41, 'mice': 15}
Result: reproduced, resting on an assumption. With recordings as units (34 stationary sessions, large box and stationary arena pooled) neither the genotype (F(1,32) = 0.24, p = 0.63) nor the interaction (F = 0.18, p = 0.67) is significant, while speed falls from the first to the second half (F = 22.8, p < 0.0001; WT 6.63 to 5.86, Fmr1-null 6.31 to 5.66 cm/s), as the paper's epoch effect. With mice as units: genotype p = 0.44, interaction p = 0.32, half p = 0.0058; with one recording per mouse and day (25): interaction p = 0.20. Within one kind of environment the interaction is significant, in opposite directions: in the large box WT slows more (by 1.37 against 0.14 cm/s; p = 0.021, 15 mice), in the stationary arena Fmr1-null slows more (1.01 against 0.25 cm/s; p = 0.0076 with recordings, 0.024 with 10 mice as units). Pooling the environments is assumed, as the STAR Methods put all four under "Constant Conditions Open-Field" and Figure S5B speaks of the open field recordings. No set of recordings here gives the 29 units the paper's 27 error degrees of freedom imply. Linearity is not judged.
Rotating arena: place cells¶
The claims for Figures 5 and S6 use the place cells recorded in the rotating arena. Which session classifies them is not stated; two readings are run. A: place cells by the classification of the rotating session itself (room frame). B: place cells by the stationary session of the same day that shares the rotating session's cut file (stationary arena 1, or 2 for M29 whose first session has another cut file), as the paper classifies cells in standard recordings.
rot = [s for s in sessions if s.env == "rotating_cylinder"]
rot_place = {}
for s in rot:
a = cells[(cells.session == s.id)]
A = set(a[a.cls == "place"].cell)
same = [x for x in sessions if x.env == "stationary_cylinder" and x.mouse == s.mouse and x.date == s.date and x.cut == s.cut]
b = cells[cells.session == same[0].id] if same else None
B = set(b[b.cls == "place"].cell) & set(s.cells) if b is not None else set()
rot_place[s.id] = {"A": sorted(A), "B": sorted(B), "B_from": same[0].id if same else None}
print(f"{s.id} ({s.genotype}): {len(s.cells)} clusters; place cells A {len(A)}, B {len(B)} (from {rot_place[s.id]['B_from']}); both {len(A & B)}")
for r in ["A", "B"]:
print(r, {g: sum(len(rot_place[s.id][r]) for s in rot if s.genotype == g) for g in ["WT", "Fmr1-null"]}, "(paper: WT 20, Fmr1-null 31)")
# Ipos per 133 ms interval, per cluster and frame
T_I = time.time()
IPOS = {}
for s in rot:
for c in s.cells:
for fr in ["Rm", "Ar"]:
IPOS[(s.id, c, fr, "abs")] = hc16.ipos_series(s, c, fr)
IPOS[(s.id, c, fr, "signed_ln")] = hc16.ipos_series(s, c, fr, signed=True, log=np.log)
print(f"Ipos computed in {time.time() - T_I:.0f} s; intervals per session:", {s.id: len(IPOS[(s.id, s.cells[0], 'Rm', 'abs')]) for s in rot})
print("Fraction of intervals with Ipos defined (room, arena):",
{s.id: (round(np.mean(~np.isnan(IPOS[(s.id, s.cells[0], 'Rm', 'abs')])), 3), round(np.mean(~np.isnan(IPOS[(s.id, s.cells[0], 'Ar', 'abs')])), 3)) for s in rot})
M18_100914_rotating (Fmr1-null): 24 clusters; place cells A 3, B 4 (from M18_100914_stationary1); both 1
M29_112414_rotating (Fmr1-null): 17 clusters; place cells A 1, B 1 (from M29_112414_stationary2); both 1
M33_051815_rotating (Fmr1-null): 2 clusters; place cells A 0, B 0 (from M33_051815_stationary1); both 0
M37_051115_rotating (Fmr1-null): 23 clusters; place cells A 3, B 3 (from M37_051115_stationary1); both 3
M16_090414_rotating (WT): 50 clusters; place cells A 4, B 4 (from M16_090414_stationary1); both 1
M28_032515_rotating (WT): 10 clusters; place cells A 1, B 2 (from M28_032515_stationary1); both 1
M32_041715_rotating (WT): 15 clusters; place cells A 4, B 4 (from M32_041715_stationary1); both 2
A {'WT': 9, 'Fmr1-null': 7} (paper: WT 20, Fmr1-null 31)
B {'WT': 10, 'Fmr1-null': 8} (paper: WT 20, Fmr1-null 31)
Ipos computed in 1 s; intervals per session: {'M18_100914_rotating': 13498, 'M29_112414_rotating': 13493, 'M33_051815_rotating': 13500, 'M37_051115_rotating': 13500, 'M16_090414_rotating': 13497, 'M28_032515_rotating': 13446, 'M32_041715_rotating': 13496}
Fraction of intervals with Ipos defined (room, arena): {'M18_100914_rotating': (np.float64(0.951), np.float64(0.943)), 'M29_112414_rotating': (np.float64(0.957), np.float64(0.944)), 'M33_051815_rotating': (np.float64(0.982), np.float64(0.961)), 'M37_051115_rotating': (np.float64(0.967), np.float64(0.949)), 'M16_090414_rotating': (np.float64(0.912), np.float64(0.893)), 'M28_032515_rotating': (np.float64(0.876), np.float64(0.857)), 'M32_041715_rotating': (np.float64(0.947), np.float64(0.93))}
fig5b-frame-specific-ipos¶
During arena rotation, room-frame positional information (Ipos) is greater in Fmr1-null than in WT place cells, and arena-frame Ipos only marginally greater.
Criterion: |Ipos| per 133 ms interval from room and arena positions (32 x 32 maps, pixels with fewer than 10 visits left out), averaged over time per cell; Mann-Whitney between genotypes; reproduced when the room-frame comparison gives p < 0.05 with Fmr1-null higher. The code's signed natural-log values are a variant.
rows = []
for s in rot:
for c in s.cells:
rows.append(dict(session=s.id, genotype=s.genotype, mouse=s.mouse, cell=c,
A=c in rot_place[s.id]["A"], B=c in rot_place[s.id]["B"],
room=np.nanmean(IPOS[(s.id, c, "Rm", "abs")]), arena=np.nanmean(IPOS[(s.id, c, "Ar", "abs")]),
room_signed_ln=np.nanmean(IPOS[(s.id, c, "Rm", "signed_ln")]),
arena_signed_ln=np.nanmean(IPOS[(s.id, c, "Ar", "signed_ln")])))
ip = pd.DataFrame(rows)
rows = []
for r in ["A", "B"]:
d = ip[ip[r]]
for m in ["room", "arena", "room_signed_ln", "arena_signed_ln"]:
a = d[d.genotype == "WT"][m]; b = d[d.genotype == "Fmr1-null"][m]
u = stats.mannwhitneyu(a, b)
rows.append(dict(reading=r, measure=m, n_WT=len(a), WT=a.mean(), WT_sem=a.sem(), n_KO=len(b), KO=b.mean(),
KO_sem=b.sem(), U=u.statistic, p=u.pvalue, KO_higher=b.mean() > a.mean(), d=hc16.cohen_d(b, a)))
f5b = pd.DataFrame(rows)
print(f5b.round(4).to_string())
print("\nBy mouse (reading A): place cells, mean room and arena Ipos")
print(ip[ip.A].groupby(["genotype", "mouse"])[["room", "arena"]].agg(["count", "mean"]).round(4).to_string())
print("Room minus arena Ipos, place cells A: WT %.4f, Fmr1-null %.4f; cells with room > arena: %d of %d"
% ((ip[ip.A & (ip.genotype == 'WT')].room - ip[ip.A & (ip.genotype == 'WT')].arena).mean(),
(ip[ip.A & (ip.genotype == 'Fmr1-null')].room - ip[ip.A & (ip.genotype == 'Fmr1-null')].arena).mean(),
int((ip[ip.A].room > ip[ip.A].arena).sum()), int(ip.A.sum())))
print("\nPer cell, mean |Ipos| (bits) in the room and arena frame, place cells of reading A or B:")
print(ip[ip.A | ip.B][["genotype", "mouse", "session", "cell", "A", "B", "room", "arena"]].sort_values(["genotype", "room"]).round(4).to_string())
print("\nReading B, room frame, leaving out one mouse at a time: Mann-Whitney p")
d = ip[ip.B]
print({m: round(stats.mannwhitneyu(d[(d.mouse != m) & (d.genotype == "WT")].room, d[(d.mouse != m) & (d.genotype == "Fmr1-null")].room).pvalue, 4)
for m in sorted(d.mouse.unique())})
reading measure n_WT WT WT_sem n_KO KO KO_sem U p KO_higher d
0 A room 9 0.0841 0.0092 7 0.1030 0.0160 20.0 0.2523 True 0.5470
1 A arena 9 0.0809 0.0096 7 0.0920 0.0131 25.0 0.5360 True 0.3523
2 A room_signed_ln 9 0.0185 0.0031 7 0.0262 0.0062 24.0 0.4698 True 0.5963
3 A arena_signed_ln 9 0.0179 0.0035 7 0.0215 0.0044 25.0 0.5360 True 0.3209
4 B room 10 0.0663 0.0094 8 0.1056 0.0133 12.0 0.0117 True 1.1760
5 B arena 10 0.0631 0.0089 8 0.0966 0.0110 13.0 0.0155 True 1.1345
6 B room_signed_ln 10 0.0133 0.0028 8 0.0259 0.0056 17.0 0.0434 True 1.0233
7 B arena_signed_ln 10 0.0123 0.0030 8 0.0223 0.0041 20.0 0.0831 True 0.9611
By mouse (reading A): place cells, mean room and arena Ipos
room arena
count mean count mean
genotype mouse
Fmr1-null M18 3 0.0626 3 0.0590
M29 1 0.1277 1 0.1262
M37 3 0.1353 3 0.1135
WT M16 4 0.0850 4 0.0842
M28 1 0.1122 1 0.1154
M32 4 0.0761 4 0.0689
Room minus arena Ipos, place cells A: WT 0.0032, Fmr1-null 0.0110; cells with room > arena: 14 of 16
Per cell, mean |Ipos| (bits) in the room and arena frame, place cells of reading A or B:
genotype mouse session cell A B room arena
21 Fmr1-null M18 M18_100914_rotating P9C12 True False 0.0446 0.0437
17 Fmr1-null M18 M18_100914_rotating P9C8 False True 0.0550 0.0549
20 Fmr1-null M18 M18_100914_rotating P9C11 False True 0.0685 0.0646
19 Fmr1-null M18 M18_100914_rotating P9C10 True False 0.0715 0.0643
18 Fmr1-null M18 M18_100914_rotating P9C9 True True 0.0716 0.0691
54 Fmr1-null M37 M37_051115_rotating P3C2 True True 0.1147 0.0924
14 Fmr1-null M18 M18_100914_rotating P9C5 False True 0.1162 0.1171
58 Fmr1-null M37 M37_051115_rotating P3C6 True True 0.1234 0.1102
33 Fmr1-null M29 M29_112414_rotating P9C3 True True 0.1277 0.1262
44 Fmr1-null M37 M37_051115_rotating P0C2 True True 0.1678 0.1379
130 WT M32 M32_041715_rotating P1C5 False True 0.0270 0.0254
100 WT M16 M16_090414_rotating P8C9 False True 0.0404 0.0410
75 WT M16 M16_090414_rotating P0C10 True True 0.0483 0.0459
117 WT M28 M28_032515_rotating P0C2 False True 0.0501 0.0503
136 WT M32 M32_041715_rotating P1C11 False True 0.0564 0.0531
98 WT M16 M16_090414_rotating P8C7 False True 0.0580 0.0536
73 WT M16 M16_090414_rotating P0C8 True False 0.0614 0.0603
128 WT M32 M32_041715_rotating P1C3 True False 0.0615 0.0520
129 WT M32 M32_041715_rotating P1C4 True True 0.0624 0.0596
126 WT M32 M32_041715_rotating P1C1 True False 0.0756 0.0735
97 WT M16 M16_090414_rotating P8C6 False True 0.1030 0.0963
133 WT M32 M32_041715_rotating P1C8 True True 0.1050 0.0907
92 WT M16 M16_090414_rotating P8C1 True False 0.1111 0.1158
124 WT M28 M28_032515_rotating P8C1 True True 0.1122 0.1154
114 WT M16 M16_090414_rotating P11C1 True False 0.1193 0.1147
Reading B, room frame, leaving out one mouse at a time: Mann-Whitney p
{'M16': np.float64(0.0426), 'M18': np.float64(0.002), 'M28': np.float64(0.0148), 'M29': np.float64(0.025), 'M32': np.float64(0.0293), 'M37': np.float64(0.1292)}
Result: partially reproduced. Room-frame Ipos is higher in Fmr1-null under both readings, but the difference is significant in one, and rests on one mouse. With place cells classified in the stationary session of the same day (reading B; 10 WT, 8 Fmr1-null) room-frame Ipos is greater in Fmr1-null (0.106 against 0.066 bits, U = 12, p = 0.012); arena-frame Ipos is greater too (0.097 against 0.063, p = 0.016), where the paper finds it only marginal. Leaving out M37 (3 of the 8 Fmr1-null cells) gives p = 0.13; leaving out any other mouse keeps p below 0.05. With place cells classified in the rotating session itself (reading A; 9 and 7) the room-frame difference has the same direction but is not significant (0.103 against 0.084, p = 0.25). The samples come from 3 mice per genotype and are about half the paper's 20 and 31 cells.
fig5c-ensemble-coordination¶
The frame-specific positional discharge fluctuations of single place cells are less correlated with those of the rest of the ensemble in Fmr1-null than in WT mice.
Criterion: per cell, delta Ipos = Ipos(room) - Ipos(arena) per 133 ms interval; the ensemble series from the frame-specific sums of Ipos over the other simultaneously recorded cells; the Pearson correlation of the cell's and the ensemble's series; delta and |delta| both run; t test on Fisher z; reproduced when, under one reading, p < 0.05 with Fmr1-null lower. The ensemble is "all simultaneously recorded cells except one" (Results); the other place cells only is a variant. Place cell readings A and B.
rows = []
for s in rot:
allc = s.cells
room = np.vstack([IPOS[(s.id, c, "Rm", "abs")] for c in allc])
arena = np.vstack([IPOS[(s.id, c, "Ar", "abs")] for c in allc])
for r in ["A", "B"]:
pcs = rot_place[s.id][r]
for c in pcs:
i = allc.index(c)
for ens_name, members in [("all clusters", [k for k in range(len(allc)) if k != i]),
("place cells", [allc.index(x) for x in pcs if x != c])]:
if len(members) == 0:
continue
d_cell = room[i] - arena[i]
d_ens = np.nansum(room[members], 0) - np.nansum(arena[members], 0)
ok = ~np.isnan(d_cell) & ~np.isnan(room[members]).all(0) & ~np.isnan(arena[members]).all(0)
for form in ["delta", "abs delta"]:
x = d_cell[ok]; y = d_ens[ok]
if form == "abs delta":
x, y = np.abs(x), np.abs(y)
rr = stats.pearsonr(x, y).statistic if x.std() > 0 and y.std() > 0 else np.nan
rows.append(dict(session=s.id, genotype=s.genotype, mouse=s.mouse, cell=c, reading=r, ensemble=ens_name,
form=form, n_ens=len(members), r=rr))
ec = pd.DataFrame(rows)
rows = []
for key, g in ec.groupby(["reading", "ensemble", "form"]):
a = g[g.genotype == "WT"].r.dropna(); b = g[g.genotype == "Fmr1-null"].r.dropna()
if len(a) < 2 or len(b) < 2:
continue
t = stats.ttest_ind(hc16.fisher(a), hc16.fisher(b))
rows.append(dict(reading=key[0], ensemble=key[1], form=key[2], n_WT=len(a), r_WT=a.mean(), sem_WT=a.sem(),
n_KO=len(b), r_KO=b.mean(), sem_KO=b.sem(), t=t.statistic, p=t.pvalue, KO_lower=b.mean() < a.mean(),
d=hc16.cohen_d(hc16.fisher(a), hc16.fisher(b))))
f5c = pd.DataFrame(rows)
print(f5c.round(4).to_string())
print("\nBy mouse (A, all clusters, abs delta):")
print(ec[(ec.reading == "A") & (ec.ensemble == "all clusters") & (ec.form == "abs delta")].groupby(["genotype", "mouse"]).r.agg(["count", "mean"]).round(3).to_string())
print("r range: %.3f to %.3f" % (ec.r.min(), ec.r.max()))
reading ensemble form n_WT r_WT sem_WT n_KO r_KO sem_KO t p KO_lower d
0 A all clusters abs delta 9 0.0704 0.0214 7 0.0713 0.0329 -0.0289 0.9773 False -0.0146
1 A all clusters delta 9 0.1301 0.0268 7 0.1566 0.0185 -0.7524 0.4643 False -0.3792
2 A place cells abs delta 8 0.0093 0.0132 6 0.0278 0.0301 -0.6208 0.5463 False -0.3353
3 A place cells delta 8 0.0532 0.0162 6 0.0985 0.0232 -1.6521 0.1244 False -0.8922
4 B all clusters abs delta 10 0.0447 0.0151 8 0.0759 0.0246 -1.1333 0.2738 False -0.5376
5 B all clusters delta 10 0.1147 0.0195 8 0.1752 0.0188 -2.1802 0.0445 False -1.0342
6 B place cells abs delta 10 0.0139 0.0084 7 0.0285 0.0183 -0.8052 0.4333 False -0.3968
7 B place cells delta 10 0.0451 0.0088 7 0.0919 0.0109 -3.3733 0.0042 False -1.6624
By mouse (A, all clusters, abs delta):
count mean
genotype mouse
Fmr1-null M18 3 0.086
M29 1 0.012
M37 3 0.076
WT M16 4 0.103
M28 1 0.046
M32 4 0.044
r range: -0.049 to 0.260
Result: not reproduced. Under every reading the Fmr1-null place cells correlate with the ensemble at least as well as the WT cells. With |delta Ipos| and all other clusters as the ensemble, r = 0.071 (Fmr1-null) against 0.070 (WT) in reading A (p = 0.98) and 0.076 against 0.045 in reading B (p = 0.27); with delta Ipos, Fmr1-null is higher in reading B with all clusters (0.175 against 0.115, p = 0.045) and with the other place cells (0.092 against 0.045, p = 0.0042).
figs6-room-frame-preference¶
Session-averaged spatial firing during rotation is better organized in the room frame than in the arena frame, similarly in both genotypes.
Criterion: a two-way genotype x environment ANOVA with repeated measures on environment (Stationary I, Stationary II, rotating room frame, rotating arena frame) on coherence and information content (log transformed; the reverse as a variant), with Tukey post hoc tests; reproduced when the environment effect has p < 0.05 for coherence with the rotating arena frame below both stationary sessions, and the interaction has p >= 0.05. Cells must be present in all four maps, so they must share the cut file across the day's three sessions (M29, whose stationary arena 1 has another cut file, drops out). Tukey's HSD is applied across the four environments without the repeated structure (statsmodels); paired t tests are shown as a variant.
import pingouin as pg
from statsmodels.stats.multicomp import pairwise_tukeyhsd
def map_measures(s, c, frame):
idx, shape = hc16.pixel_index(s, frame)
rm, o = hc16.rate_map(s, c, idx, shape)
return hc16.coherence(rm), hc16.information(rm, o)
rows = []
for s in rot:
st = {x.cond: x for x in sessions if x.env == "stationary_cylinder" and x.mouse == s.mouse and x.date == s.date and x.cut == s.cut}
if not {"stationary1", "stationary2"} <= set(st):
print(f"{s.id}: no stationary arena 1 and 2 with the same cut file; left out")
continue
for r in ["A", "B"]:
for c in rot_place[s.id][r]:
if c not in st["stationary1"].cells or c not in st["stationary2"].cells:
continue
for env, (ss, fr) in {"Stationary I": (st["stationary1"], "Rm"), "Stationary II": (st["stationary2"], "Rm"),
"Rotating room": (s, "Rm"), "Rotating arena": (s, "Ar")}.items():
coh, inf = map_measures(ss, c, fr)
rows.append(dict(reading=r, session=s.id, genotype=s.genotype, mouse=s.mouse, cell=f"{s.id}.{c}", env=env,
coherence=coh, info=inf, log_info=np.log(inf), log_coherence=np.log(coh) if coh > 0 else np.nan))
fs6 = pd.DataFrame(rows)
for r in ["A", "B"]:
d = fs6[fs6.reading == r].dropna(subset=["coherence", "log_info"])
full = d.groupby("cell").env.nunique() == 4
d = d[d.cell.isin(full[full].index)]
print(f"\nReading {r}: cells with all four maps: {d.groupby('genotype').cell.nunique().to_dict()} (paper: WT 20, Fmr1-null 31)")
print(d.groupby(["env", "genotype"])[["coherence", "info"]].mean().round(3).unstack().to_string())
for m in ["coherence", "log_info", "info", "log_coherence"]:
dd = d.dropna(subset=[m])
keep = dd.groupby("cell").env.nunique() == 4
dd = dd[dd.cell.isin(keep[keep].index)]
a = pg.mixed_anova(data=dd, dv=m, within="env", between="genotype", subject="cell")
pcol = [c for c in a.columns if c.startswith("p")][0]
print(f" {m}: " + "; ".join(f"{row.Source} F({row.DF1},{row.DF2}) = {row.F:.2f}, p = {row[pcol]:.4g}" for _, row in a.iterrows()))
if m in ["coherence", "log_info"]:
tk = pairwise_tukeyhsd(dd[m], dd.env)
print(" Tukey HSD:\n" + "\n".join(" " + l for l in str(tk.summary()).splitlines()[2:]))
w = dd.pivot(index="cell", columns="env", values=m)
for e in ["Stationary I", "Stationary II"]:
tt = stats.ttest_rel(w["Rotating arena"], w[e])
print(f" paired t, Rotating arena vs {e}: mean diff {np.mean(w['Rotating arena'] - w[e]):.3f}, t = {tt.statistic:.2f}, p = {tt.pvalue:.4g}")
M29_112414_rotating: no stationary arena 1 and 2 with the same cut file; left out
Reading A: cells with all four maps: {'Fmr1-null': 6, 'WT': 9} (paper: WT 20, Fmr1-null 31)
coherence info
genotype Fmr1-null WT Fmr1-null WT
env
Rotating arena 0.394 0.423 1.373 1.371
Rotating room 0.753 0.532 1.652 1.481
Stationary I 0.536 0.464 2.590 1.958
Stationary II 0.687 0.466 1.592 1.730
coherence: genotype F(1,13) = 2.28, p = 0.1551; env F(3,39) = 2.58, p = 0.06716; Interaction F(3,39) = 1.18, p = 0.3309
Tukey HSD:
group1 group2 meandiff p-adj lower upper reject
------------------------------------------------------------------
Rotating arena Rotating room 0.209 0.1031 -0.0284 0.4463 False
Rotating arena Stationary I 0.0816 0.7993 -0.1557 0.3189 False
Rotating arena Stationary II 0.1428 0.3909 -0.0946 0.3801 False
Rotating room Stationary I -0.1274 0.4917 -0.3647 0.11 False
Rotating room Stationary II -0.0662 0.881 -0.3035 0.1711 False
Stationary I Stationary II 0.0612 0.9034 -0.1762 0.2985 False
------------------------------------------------------------------
paired t, Rotating arena vs Stationary I: mean diff -0.082, t = -0.96, p = 0.352
paired t, Rotating arena vs Stationary II: mean diff -0.143, t = -2.10, p = 0.05396
log_info: genotype F(1,13) = 0.02, p = 0.8791; env F(3,39) = 12.68, p = 6.261e-06; Interaction F(3,39) = 2.38, p = 0.0839
Tukey HSD:
group1 group2 meandiff p-adj lower upper reject
------------------------------------------------------------------
Rotating arena Rotating room 0.1238 0.9199 -0.3913 0.6388 False
Rotating arena Stationary I 0.4359 0.1248 -0.0791 0.9509 False
Rotating arena Stationary II 0.2277 0.6478 -0.2874 0.7427 False
Rotating room Stationary I 0.3122 0.3843 -0.2029 0.8272 False
Rotating room Stationary II 0.1039 0.9503 -0.4111 0.6189 False
Stationary I Stationary II -0.2082 0.7087 -0.7233 0.3068 False
------------------------------------------------------------------
paired t, Rotating arena vs Stationary I: mean diff -0.436, t = -5.16, p = 0.0001439
paired t, Rotating arena vs Stationary II: mean diff -0.228, t = -3.14, p = 0.007253
info: genotype F(1,13) = 0.16, p = 0.699; env F(3,39) = 7.03, p = 0.0006865; Interaction F(3,39) = 1.45, p = 0.2436
log_coherence: genotype F(1,13) = 0.14, p = 0.7111; env F(3,39) = 2.48, p = 0.07547; Interaction F(3,39) = 0.64, p = 0.5966
Reading B: cells with all four maps: {'Fmr1-null': 7, 'WT': 10} (paper: WT 20, Fmr1-null 31)
coherence info
genotype Fmr1-null WT Fmr1-null WT
env
Rotating arena 0.334 0.260 1.022 1.741
Rotating room 0.527 0.338 1.182 1.883
Stationary I 0.592 0.581 1.544 1.948
Stationary II 0.506 0.352 1.196 2.157
coherence: genotype F(1,15) = 1.14, p = 0.3028; env F(3,45) = 6.24, p = 0.001241; Interaction F(3,45) = 0.66, p = 0.5798
Tukey HSD:
group1 group2 meandiff p-adj lower upper reject
------------------------------------------------------------------
Rotating arena Rotating room 0.1251 0.5249 -0.1164 0.3666 False
Rotating arena Stationary I 0.2947 0.0107 0.0531 0.5362 True
Rotating arena Stationary II 0.1243 0.5301 -0.1172 0.3659 False
Rotating room Stationary I 0.1696 0.2591 -0.072 0.4111 False
Rotating room Stationary II -0.0008 1.0 -0.2423 0.2408 False
Stationary I Stationary II -0.1703 0.2555 -0.4119 0.0712 False
------------------------------------------------------------------
paired t, Rotating arena vs Stationary I: mean diff -0.295, t = -3.81, p = 0.001542
paired t, Rotating arena vs Stationary II: mean diff -0.124, t = -2.06, p = 0.05643
log_info: genotype F(1,15) = 3.00, p = 0.104; env F(3,45) = 6.60, p = 0.0008615; Interaction F(3,45) = 1.99, p = 0.1285
Tukey HSD:
group1 group2 meandiff p-adj lower upper reject
------------------------------------------------------------------
Rotating arena Rotating room 0.1087 0.95 -0.4268 0.6443 False
Rotating arena Stationary I 0.2551 0.5936 -0.2804 0.7907 False
Rotating arena Stationary II 0.2066 0.7397 -0.3289 0.7422 False
Rotating room Stationary I 0.1464 0.8884 -0.3892 0.682 False
Rotating room Stationary II 0.0979 0.9628 -0.4377 0.6335 False
Stationary I Stationary II -0.0485 0.9952 -0.5841 0.4871 False
------------------------------------------------------------------
paired t, Rotating arena vs Stationary I: mean diff -0.255, t = -3.27, p = 0.004766
paired t, Rotating arena vs Stationary II: mean diff -0.207, t = -3.21, p = 0.00549
info: genotype F(1,15) = 2.79, p = 0.1154; env F(3,45) = 3.78, p = 0.01684; Interaction F(3,45) = 1.91, p = 0.1414
log_coherence: genotype F(1,13) = 0.87, p = 0.3673; env F(3,39) = 5.47, p = 0.003089; Interaction F(3,39) = 0.44, p = 0.7257
Result: partially reproduced, resting on an assumption. In reading B (10 WT and 7 Fmr1-null place cells with all four maps) coherence has an environment effect (F(3,45) = 6.24, p = 0.0012) and no interaction (p = 0.58); Tukey's test puts the rotating arena frame below stationary arena I (difference 0.29, adjusted p = 0.011) but not below stationary arena II (0.12, p = 0.53; paired t p = 0.056). Log information has an environment effect (p = 0.0009) and no interaction (p = 0.13). In reading A (9 and 6 cells) the coherence environment effect is not significant (p = 0.067). The outcome adopts reading B, an assumption. It settles a question the record leaves open elsewhere: fig5b-frame-specific-ipos is judged on both readings, and the question is put to the paper's authors. Under reading A this claim would not be reproduced.
Summary¶
| Claim | Outcome |
|---|---|
| fig1ab-baseline-transmission | blocked: data missing (slice recordings) |
| fig1cd-potentiation | blocked: data missing (slice recordings) |
| figs1-avoidance-equal | blocked: data missing (slice-cohort behaviour) |
| unit-and-cell-counts | blocked: data missing (15 of 21 mice; no isolation measures) |
| interneuron-proportion | partially reproduced (direction holds; percentages far higher) |
| place-cell-proportion | not reproduced |
| figs3-nonspatial-properties-similar | partially reproduced (rate differs) |
| fig2-spatial-quality-similar | not reproduced (genotype effects, mostly in interneurons) |
| tables1-place-cell-properties-similar | partially reproduced (place cells alike; interneuron rate differs) |
| tables1-nonspatial-active-pixels | not reproduced |
| figs4-remapping-similar | blocked: data missing (no square and circular recordings of the same cells) |
| fig3b-place-cell-modulation-similar, fig3bc-weaker-modulation-nonspatial-interneurons, fig3b-phase-preference, fig3d-stereotypy, figs5a-power-spectra-similar | blocked: data missing (no LFP) |
| overdispersion-passes | not reproduced (literal pass rule; the count rule meets the criterion in R2, asked of a reader) |
| overdispersion-cells | not reproduced |
| fig4b-covariance-30-passes | partially reproduced |
| fig4b-covariance-40-passes | partially reproduced |
| fig4cd-kendall-place-cells | not reproduced |
| fig4d-network-state-recurrence | not reproduced |
| kendall-nonspatial-pyramidal | not reproduced |
| kendall-quiet-states | not reproduced |
| locomotion-similar | reproduced (assumption: environments pooled) |
| fig5b-frame-specific-ipos | partially reproduced (significant in one reading, resting on one mouse) |
| fig5c-ensemble-coordination | not reproduced |
| figs6-room-frame-preference | partially reproduced (assumption: cells classified in the stationary session) |
| fig6-covariance-sem-predicts-errors, fig6-kendall-predicts-errors, discussion-single-cell-measures-no-relation, fig6-avoidance-behavior | blocked: data missing (avoidance sessions) |
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 paper's cells are units with IsoIBG and IsoINN of at least 4 bits. The data set has no isolation measures and no waveforms, so every sorted cluster other than cluster 0 is used, and cells are classified by rate and bursting without waveform width. Table S2 lists fewer cells per session than the data set has clusters (for example 15 cells for M16 in stationary arena 1, against 50 clusters here), so the population here includes clusters the paper excluded. This bears on every tested claim. A refractory filter, a weak proxy for isolation, removes 70 of 774 clusters. Among the single-cell claims it removes the rate effect of figs3-nonspatial-properties-similar and, in R1, the significance of the interneuron difference, and leaves the other results as they are. Run on the pass and Kendall claims (R2), it leaves their results as they are. It was not run on fig4d-network-state-recurrence, kendall-quiet-states, locomotion-similar or the rotating arena claims.
- The data set holds 8 WT and 7 Fmr1-null mice, the large box and the stationary and rotating arena. The small box, the ring, sleep recordings, LFP, slice data and avoidance sessions are not in it.
- The spatial scale is assumed from the tracked extent and the enclosure sizes of the Methods. On the code's 32 x 32 grid, which needs no scale, 45 of 774 clusters change class, and the outcome of no single-cell claim changes. On the grid, under the literal pass rule, the results of the pass and Kendall claims (R2) are as on 1.25 cm pixels; under the count rule fig4b-covariance-40-passes meets its criterion. The grid was not run on fig4d-network-state-recurrence, kendall-quiet-states, locomotion-similar or the rotating arena claims (there Ipos is computed on the 32 x 32 grid, but the place cells are classified on 1.25 cm pixels).
- The pass rule of the overdispersion claims is read literally, as the claims file does; it passes most 5 s intervals. The count expected at the mean rate over 5 s, a variant, changes the overdispersion-passes result in R2 (not in R1, and not overdispersion-cells), and with the 32 x 32 grid the fig4b-covariance-40-passes result; it is asked of a reader.
- The session that classifies a cell is open (R1, R2; A, B for the rotating arena); the results under each are printed. A cluster name within a day's cut file is taken to be one cell. R2 counts the cells and pairs of stationary arena 1 and 2 of a day twice; R1 counts them once.
What was found about the data set:
- It holds part of the paper's data: 15 of 21 mice, three of the four environments, no LFP, no sleep or rest recordings, no avoidance sessions, and no slice recordings.
- No isolation measures, waveforms or cell classes are given, so the paper's cells cannot be selected; the files hold every cluster, including cluster 0 (unsorted spikes). The TS headers hold only the sampling rate, the first timestamp and a track-to-map scale of 1.00.
- The descriptor gives no spatial scale for the 0-255 pixel positions, and the scale differs between box sessions (about 3.6 and 4.4 px/cm by extent).
- The stated position sampling rate drifts against the spike clock (spikes up to 118 samples, about 4 s, after the nominal time of their position sample); spikes are filed under the right sample, so the files are consistent, but the nominal sample times are not usable as times.
- Six spike trains have one backward step in their times (up to 1.4 s).
- The 0-15 and 15-30 min files repeat the full sessions except one position sample at the split,
which is in neither part, so a spike filed under it is lost; the
.timesfiles repeat the TS spike times. - The descriptor's example header gives 58.3 samples/s; every file has 30.0.
print(f"Notebook run time: {(time.time() - T0) / 60:.1f} min")
Notebook run time: 2.3 min