Reproducing Chen, He et al. (2026) from dandiset 001933¶
Paper: Chen C, He Q, Tombesi G, et al. (2026). Leucine-rich repeat kinase 2 impairs the release sites of Parkinson's disease vulnerable dopamine axons. Nature Communications. doi:10.1038/s41467-026-75194-3. This attempt used the open access full text (CC BY) and its Supplementary Information.
Dandiset: DANDI:001933, version 0.260825.1457. The paper does not name the dandiset or a version (its data statement points to Zenodo, PRIDE and the BioImage Archive). This is the only published version, and it holds the same 27 files as the draft (checked in the data check), so it is pinned.
What the dandiset holds: one session per mouse of GRAB-DA3m fiber photometry in the dorsal striatum during optogenetic stimulation of SNc dopamine neurons, 15 Anxa1-iCre mice (dorsolateral striatum) and 12 Calb1-Cre mice (dorsomedial striatum), LRRK2-WT and LRRK2-G2019S (GS). This is the data of Figure 6 and Supplementary Figures 7 and 8. The immunostaining, proteomics, western blots and super-resolution imaging of Figures 1 to 5 are not in it.
Outside input: the paper's source data file (Zenodo 10.5281/zenodo.19930567,
Source data.xlsx, md5 checked below), used only in the claim source-data-link to test whether the dandiset's
recordings are those behind the published values. Every other claim runs on the dandiset alone. The paper's analysis
code was read at DombeckLab/lrrk2_photometry_analysis,
commit d34558f (the Zenodo release 10.5281/zenodo.20244434 is commit 1b551d1, which differs only by a licence file).
| Claim | Paper | Tested |
|---|---|---|
source-data-link |
Source data of Fig. 6, Supp. Figs 7 and 8 can be recomputed from the dandiset | tested (with the source data) |
fig6e-anxa1-evoked-reduced |
Anxa1 evoked release lower in GS at 0.5 mW (p = 1.10e-20) | tested |
fig6e-anxa1-evoked-per-mouse |
7 of 8 Anxa1 GS mice reduced; 3743 not | tested |
supp7c-anxa1-evoked-all-powers |
Reduction at 0.1, 0.5, 1 and 4 mW | tested |
fig6f-anxa1-spontaneous-reduced |
Anxa1 spontaneous transients smaller in GS (1064 against 798) | tested |
fig6f-anxa1-spontaneous-per-mouse |
6 of 8 Anxa1 GS mice reduced; 4651 and 3743 not | tested |
supp8c-calb1-spontaneous-not-reduced |
Calb1 spontaneous transients not reduced (p = 0.98) | tested |
supp8e-calb1-evoked-reduced |
Calb1 evoked release reduced at four powers | tested |
supp8e-calb1-few-mice-reduced |
Only 2 of 7 Calb1 GS mice reduced | tested |
cohens-d-anxa1-larger |
Cohen's d at 0.5 mW, Anxa1 2.05 against Calb1 0.76 | tested |
fig1d-lrrk2-enriched-vulnerable and seven other claims of Figs 1 to 5 |
Imaging, proteomics, western blots | blocked: not in the dandiset |
Method choices are fixed in the claims file, claims/001933/10.1038-s41467-026-75194-3.yaml. In short: the signal is
the dandiset's processed dF/F, the trace the paper's code analyses; the evoked integral follows the Methods (the
light-on period), with the code's baseline; spontaneous transients follow the Methods and transient_by_threshold.m.
import time
import warnings
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import stats
import lrrk2
pd.set_option("display.width", 220)
pd.set_option("display.max_columns", 40)
pd.set_option("display.max_rows", 200)
warnings.filterwarnings("ignore", category=RuntimeWarning)
started = time.time()
sessions = lrrk2.load_all()
by_mouse = {s["mouse"]: s for s in sessions}
print(f"loaded {len(sessions)} files from dandiset {lrrk2.DANDISET_ID} version {lrrk2.DANDISET_VERSION} "
f"in {time.time() - started:.0f} s")
# No step of this analysis draws random numbers, so no generator is needed.
loaded 27 files from dandiset 001933 version 0.260825.1457 in 50 s
Data check¶
Each check of the description against the values is stated under its output.
info = pd.DataFrame([{
"mouse": s["mouse"], "subject_id": s["subject_id"], "line": s["line"], "group": s["group"],
"genotype": s["genotype"], "sex": s["sex"], "date_of_birth": s["date_of_birth"][:10],
"session_start": s["session_start"][:19],
"age_months": round((pd.Timestamp(s["session_start"][:10]) - pd.Timestamp(s["date_of_birth"][:10])).days / 30.44, 1),
"recorded_in": s["location"][s["dff_region"]], "species": s["species"], "strain": s["strain"],
"dff_md5": s["md5"][:12]} for s in sessions]).sort_values(["line", "group", "mouse"])
print("Files (one session each) by Cre line and genotype:")
print(pd.crosstab(info.line, info.group, margins=True).to_string())
print("\nPaper: Anxa1 8 G2019S and 7 WT mice (Figure 6C); Calb1 7 G2019S and 5 WT mice (Supplementary Figure 8D),"
"\nafter excluding one Anxa1 G2019S, one Calb1 G2019S and two Calb1 WT mice (Methods).")
paper_anxa1_gs = [4885, 4651, 4652, 3742, 3743, 4004, 4005, 4007] # mouse IDs in the Figure 6E legend
ours = sorted(info[(info.line == "Anxa1") & (info.group == "GS")].mouse)
print("Anxa1 G2019S mouse IDs in the Figure 6E legend found in the dandiset:", sorted(paper_anxa1_gs) == ours, ours)
print("\nSex by line and genotype:")
print(pd.crosstab([info.line, info.group], info.sex).to_string())
print("\nAge at the session (months), by line: ", info.groupby("line").age_months.agg(["min", "max"]).to_dict("index"))
print("Recording site by line:", info.groupby("line").recorded_in.unique().to_dict())
print("Species and strain:", info.species.unique().tolist(), info.strain.unique().tolist())
print("\nDistinct dF/F traces (md5 of the full series):", info.dff_md5.nunique(), "of", len(info),
"; distinct session start times:", info.session_start.nunique())
print("\n", info.to_string(index=False))
# The pinned version against the draft: same files and content?
from dandi.dandiapi import DandiAPIClient
client = DandiAPIClient()
def digests(version):
return {a.path: (a.size, a.get_raw_metadata()["digest"].get("dandi:dandi-etag")) for a in client.get_dandiset(lrrk2.DANDISET_ID, version).get_assets()}
pinned, draft = digests(lrrk2.DANDISET_VERSION), digests("draft")
print(f"\nAssets: pinned version {len(pinned)}, draft {len(draft)}; identical paths, sizes and etags: {pinned == draft}")
Files (one session each) by Cre line and genotype:
group GS WT All
line
Anxa1 8 7 15
Calb1 7 5 12
All 15 12 27
Paper: Anxa1 8 G2019S and 7 WT mice (Figure 6C); Calb1 7 G2019S and 5 WT mice (Supplementary Figure 8D),
after excluding one Anxa1 G2019S, one Calb1 G2019S and two Calb1 WT mice (Methods).
Anxa1 G2019S mouse IDs in the Figure 6E legend found in the dandiset: True [3742, 3743, 4004, 4005, 4007, 4651, 4652, 4885]
Sex by line and genotype:
sex F M
line group
Anxa1 GS 5 3
WT 2 5
Calb1 GS 4 3
WT 3 2
Age at the session (months), by line: {'Anxa1': {'min': 6.3, 'max': 7.6}, 'Calb1': {'min': 6.4, 'max': 7.5}}
Recording site by line: {'Anxa1': <StringArray>
['dorsal lateral striatum']
Length: 1, dtype: str, 'Calb1': <StringArray>
['dorsal medial striatum']
Length: 1, dtype: str}
Species and strain: ['Mus musculus'] ['C57BL/6J']
Distinct dF/F traces (md5 of the full series): 27 of 27 ; distinct session start times: 27
mouse subject_id line group genotype sex date_of_birth session_start age_months recorded_in species strain dff_md5
3742 3742-anxa-gs Anxa1 GS LRRK2-G2019S M 2024-06-22 2025-01-24T13:57:40 7.1 dorsal lateral striatum Mus musculus C57BL/6J 32910da94bb5
3743 3743-anxa-gs Anxa1 GS LRRK2-G2019S F 2024-06-22 2025-01-24T12:58:18 7.1 dorsal lateral striatum Mus musculus C57BL/6J c8197bb500c9
4004 4004-anxa-gs Anxa1 GS LRRK2-G2019S F 2025-01-13 2025-08-13T14:33:49 7.0 dorsal lateral striatum Mus musculus C57BL/6J 61b9d61f611d
4005 4005-anxa-gs Anxa1 GS LRRK2-G2019S F 2025-01-13 2025-08-13T15:03:13 7.0 dorsal lateral striatum Mus musculus C57BL/6J b702c1a8ef7d
4007 4007-anxa-gs Anxa1 GS LRRK2-G2019S M 2025-01-13 2025-08-13T16:24:04 7.0 dorsal lateral striatum Mus musculus C57BL/6J a877b1b28c63
4651 4651-anxa-gs Anxa1 GS LRRK2-G2019S F 2024-12-19 2025-07-02T10:24:51 6.4 dorsal lateral striatum Mus musculus C57BL/6J 81bd1ddec442
4652 4652-anxa-gs Anxa1 GS LRRK2-G2019S M 2024-10-30 2025-05-20T12:09:39 6.6 dorsal lateral striatum Mus musculus C57BL/6J 5f8f49231d9e
4885 4885-anxa-gs Anxa1 GS LRRK2-G2019S F 2024-07-06 2025-01-24T11:26:46 6.6 dorsal lateral striatum Mus musculus C57BL/6J 52017ceb29e4
3947 3947-anxa-wt Anxa1 WT LRRK2-WT M 2024-07-06 2025-01-27T12:37:54 6.7 dorsal lateral striatum Mus musculus C57BL/6J 13efb67f38fa
3948 3948-anxa-wt Anxa1 WT LRRK2-WT M 2024-07-06 2025-01-27T13:32:06 6.7 dorsal lateral striatum Mus musculus C57BL/6J 45aa0e733966
4253 4253-anxa-wt Anxa1 WT LRRK2-WT F 2025-01-13 2025-08-14T15:17:39 7.0 dorsal lateral striatum Mus musculus C57BL/6J ba6b51c08785
4398 4398-anxa-wt Anxa1 WT LRRK2-WT M 2024-09-30 2025-05-02T15:47:48 7.0 dorsal lateral striatum Mus musculus C57BL/6J 2bca264e0b13
4401 4401-anxa-wt Anxa1 WT LRRK2-WT M 2024-12-23 2025-07-02T11:58:30 6.3 dorsal lateral striatum Mus musculus C57BL/6J 2b5b0af5e35d
4558 4558-anxa-wt Anxa1 WT LRRK2-WT F 2024-09-30 2025-05-20T11:23:39 7.6 dorsal lateral striatum Mus musculus C57BL/6J 03ca39f90a50
4561 4561-anxa-wt Anxa1 WT LRRK2-WT M 2024-12-19 2025-07-02T11:08:51 6.4 dorsal lateral striatum Mus musculus C57BL/6J bba28e200248
302 302-calb-gs Calb1 GS LRRK2-G2019S M 2025-04-03 2025-11-10T13:29:32 7.3 dorsal medial striatum Mus musculus C57BL/6J 45357f497b8a
306 306-calb-gs Calb1 GS LRRK2-G2019S M 2025-04-03 2025-11-10T15:06:39 7.3 dorsal medial striatum Mus musculus C57BL/6J e1e60b6257be
740 740-calb-gs Calb1 GS LRRK2-G2019S F 2025-08-09 2026-03-24T11:25:42 7.5 dorsal medial striatum Mus musculus C57BL/6J b4ae1bcbb642
741 741-calb-gs Calb1 GS LRRK2-G2019S F 2025-08-09 2026-03-24T11:50:36 7.5 dorsal medial striatum Mus musculus C57BL/6J 32944d44a305
848 848-calb-gs Calb1 GS LRRK2-G2019S F 2025-05-19 2025-12-17T13:13:20 7.0 dorsal medial striatum Mus musculus C57BL/6J abb262188482
852 852-calb-gs Calb1 GS LRRK2-G2019S F 2025-05-19 2025-12-17T12:04:48 7.0 dorsal medial striatum Mus musculus C57BL/6J 4df069801545
935 935-calb-gs Calb1 GS LRRK2-G2019S M 2025-10-07 2026-04-23T12:35:42 6.5 dorsal medial striatum Mus musculus C57BL/6J 97d06653b3c4
671 671-calb-wt Calb1 WT LRRK2-WT F 2025-08-31 2026-03-19T10:41:31 6.6 dorsal medial striatum Mus musculus C57BL/6J 8db992bcd20c
673 673-calb-wt Calb1 WT LRRK2-WT F 2025-08-31 2026-03-19T11:49:31 6.6 dorsal medial striatum Mus musculus C57BL/6J c59715218cc0
688 688-calb-wt Calb1 WT LRRK2-WT M 2025-05-08 2025-12-17T13:38:53 7.3 dorsal medial striatum Mus musculus C57BL/6J ad3bb702e244
966 966-calb-wt Calb1 WT LRRK2-WT M 2025-10-09 2026-04-23T12:11:19 6.4 dorsal medial striatum Mus musculus C57BL/6J 982088216b0d
1803 1803-calb-wt Calb1 WT LRRK2-WT F 2025-04-11 2025-11-10T12:15:28 7.0 dorsal medial striatum Mus musculus C57BL/6J af5a0b79fc48
Assets: pinned version 27, draft 27; identical paths, sizes and etags: True
The files hold one session each of 15 Anxa1 mice (8 GS, 7 WT) and 12 Calb1 mice (7 GS, 5 WT), the paper's counts after its exclusions, and the Anxa1 GS mouse IDs are those of the Figure 6E legend. The four excluded mice are not in the dandiset, which does not say that they were left out. Ages at the session are 6.3 to 7.6 months, consistent with surgery at 6 months and recording four weeks later. Anxa1 mice are recorded in the dorsolateral and Calb1 mice in the dorsomedial striatum, as the paper states. No two files share a dF/F trace or a start time, so no recording is deposited twice. The pinned version and the draft hold the same files.
rows = []
for s in sessions:
t, x = s["t"], s["dff"]
dt = np.diff(t) * 1000
rows.append({"mouse": s["mouse"], "line": s["line"], "group": s["group"], "samples": len(t),
"first_s": round(t[0], 2), "minutes": round((t[-1] - t[0]) / 60, 1),
"dt_med_ms": round(np.median(dt), 2), "dt_min_ms": round(dt.min(), 2), "dt_max_ms": round(dt.max(), 2),
"dff_min": round(x.min(), 1), "dff_p8": round(np.percentile(x, 8), 2), "dff_max": round(x.max(), 1),
"dff405_min": round(s["dff405"].min(), 1), "dff405_max": round(s["dff405"].max(), 1),
"max_diff_recomputed": round(np.abs(x - s["dff_recomputed"]).max(), 4),
"raw_lag_samples": s["raw_lag"], "raw_r_at_lag0": round(s["raw_r"], 3),
"raw_470_samples_per_bin": round(s["raw_n"] / len(t), 2)})
series = pd.DataFrame(rows).sort_values(["line", "group", "mouse"])
print("dF/F series: stated unit", repr(sessions[0]["dff_unit"]), "; description:", sessions[0]["dff_description"].strip())
print(series.to_string(index=False))
print("\nSampling interval over all files (ms): median", series.dt_med_ms.median(), "min", series.dt_min_ms.min(),
"max", series.dt_max_ms.max())
print("Recording length (min):", series.minutes.min(), "to", series.minutes.max(), "(Methods: 20 min)")
print("Largest difference between the stored dF/F and the code's df_f plus 20-sample moving mean recomputed from the",
"stored corrected signal (% dF/F):", series.max_diff_recomputed.max())
print("Lag that best aligns the raw 470 nm signal, averaged into each 100 Hz bin, with the corrected signal:",
sorted(series.raw_lag_samples.unique()), "; correlation at lag 0 from", series.raw_r_at_lag0.min(), "to",
series.raw_r_at_lag0.max())
dF/F series: stated unit 'a.u.' ; description: % ΔF/F of baseline-corrected GRAB-DA3m 470 nm fluorescence (Calb-LRRK2 group, DMS) at 100 Hz. F0 = 8th percentile of the corrected trace. Smoothed with a 20-sample moving mean (~0.2 s). mouse line group samples first_s minutes dt_med_ms dt_min_ms dt_max_ms dff_min dff_p8 dff_max dff405_min dff405_max max_diff_recomputed raw_lag_samples raw_r_at_lag0 raw_470_samples_per_bin 3742 Anxa1 GS 148937 1.93 24.8 10.0 9.5 10.0 -31.7 1.36 149.1 -2.3 50.0 0.0000 0 0.891 8.03 3743 Anxa1 GS 148293 2.47 24.7 10.0 9.5 10.0 -57.9 1.24 553.5 -4.6 15.0 0.0059 0 0.966 8.01 4004 Anxa1 GS 131835 24.74 22.0 10.0 9.5 10.0 -10.0 0.42 116.2 -3.4 4.5 0.0011 0 0.917 8.17 4005 Anxa1 GS 135709 25.94 22.6 10.0 9.5 10.0 -5.8 0.24 76.0 -3.3 8.2 0.0000 0 0.770 8.20 4007 Anxa1 GS 129307 21.06 21.5 10.0 9.5 10.0 -6.8 0.34 117.2 -2.1 5.8 0.0000 0 0.941 8.16 4651 Anxa1 GS 123899 24.68 20.6 10.0 9.5 10.0 -28.3 0.28 313.4 -2.0 11.1 0.0021 0 0.888 8.17 4652 Anxa1 GS 130637 22.54 21.8 10.0 9.5 10.0 -38.4 0.41 356.6 -4.0 17.4 0.0011 0 0.985 8.15 4885 Anxa1 GS 140790 2.34 23.5 10.0 9.5 10.0 -14.0 0.42 248.7 -4.8 18.8 0.0020 0 0.950 8.01 3947 Anxa1 WT 145600 2.24 24.3 10.0 9.5 10.0 -46.2 0.44 567.9 -6.1 13.3 0.0006 0 0.901 8.03 3948 Anxa1 WT 152720 1.94 25.4 10.0 9.5 10.0 -56.6 1.24 693.1 -5.2 14.2 0.0035 0 0.983 8.02 4253 Anxa1 WT 126423 29.97 21.1 10.0 9.5 10.0 -37.4 0.90 485.2 -3.6 6.0 0.0088 0 0.940 8.21 4398 Anxa1 WT 122304 28.30 20.4 10.0 9.5 10.0 -44.8 0.96 677.5 -3.5 9.9 0.0019 0 0.993 8.29 4401 Anxa1 WT 128603 25.68 21.4 10.0 9.5 10.0 -78.4 0.72 597.0 -3.5 13.0 0.0003 0 0.910 8.18 4558 Anxa1 WT 125326 26.48 20.9 10.0 9.5 10.0 -34.8 0.63 573.4 -3.0 11.5 0.0006 0 0.980 8.19 4561 Anxa1 WT 132088 27.27 22.0 10.0 9.5 10.0 -18.0 0.69 278.5 -2.3 5.1 0.0010 0 0.979 8.18 302 Calb1 GS 129360 24.87 21.6 10.0 9.5 10.0 -42.2 1.17 767.8 -3.2 13.1 0.0000 0 0.981 8.17 306 Calb1 GS 123782 19.52 20.6 10.0 9.5 10.0 -53.7 0.38 502.3 -3.0 10.2 0.0000 0 0.912 8.14 740 Calb1 GS 129340 24.48 21.6 10.0 9.5 10.0 -39.5 1.09 349.4 -3.4 10.4 0.0012 0 0.950 8.16 741 Calb1 GS 134827 2.89 22.5 10.0 9.5 10.0 -43.0 1.58 669.6 -4.0 11.2 0.0007 0 0.983 8.03 848 Calb1 GS 124741 20.36 20.8 10.0 9.5 10.0 -38.7 0.79 505.5 -4.6 10.3 0.0019 0 0.963 8.15 852 Calb1 GS 128578 3.90 21.4 10.0 9.5 10.0 -40.8 1.47 671.2 -2.2 11.8 0.0044 0 0.973 8.04 935 Calb1 GS 122988 44.12 20.5 10.0 9.5 10.0 -47.8 1.54 542.8 -2.5 8.0 0.0009 0 0.964 8.30 671 Calb1 WT 127644 3.24 21.3 10.0 9.5 10.0 -50.8 1.57 779.7 -2.3 9.0 0.0258 0 0.993 8.03 673 Calb1 WT 125288 25.53 20.9 10.0 9.5 10.0 -35.7 1.61 325.0 -2.1 7.0 0.0000 0 0.958 8.18 688 Calb1 WT 124290 2.68 20.7 10.0 9.5 10.0 -49.6 0.96 943.6 -5.2 12.1 0.0022 0 0.952 8.03 966 Calb1 WT 123499 28.54 20.6 10.0 9.5 10.0 -50.9 0.92 657.7 -2.3 8.1 0.0058 0 0.978 8.20 1803 Calb1 WT 149832 2.73 25.0 10.0 9.5 10.0 -56.0 1.35 1281.9 -5.0 13.8 0.0027 0 0.986 8.03 Sampling interval over all files (ms): median 10.0 min 9.5 max 10.0 Recording length (min): 20.4 to 25.4 (Methods: 20 min) Largest difference between the stored dF/F and the code's df_f plus 20-sample moving mean recomputed from the stored corrected signal (% dF/F): 0.0258 Lag that best aligns the raw 470 nm signal, averaged into each 100 Hz bin, with the corrected signal: [np.int64(0)] ; correlation at lag 0 from 0.77 to 0.993
The processed series are regular at 10 ms (the shortest interval, 9.5 ms, comes from the 2 kHz grid) and last 20.4 to
25.4 min, a little longer than the Methods' 20 min. The dF/F series is stated in a.u., but its description and its
values are % dF/F: the code's df_f and 20-sample moving mean applied to the stored corrected signal give the stored
series to within 0.026 % dF/F. The raw 470 nm signal, averaged into the 100 Hz bins, aligns with the corrected signal
at lag 0 in every file (correlation 0.77 to 0.99), so raw and processed series share a clock. About 8 of the 20 raw
samples per bin are kept for 470 nm, as the demultiplexing describes.
code_letter = {0.1: "a", 0.25: "b", 0.5: "c", 1.0: "d", 1.5: "e", 2.0: "f", 3.0: "g", 4.0: "h"}
rows = []
for s in sessions:
p = s["power_in_mW"]
dur = s["stop_time"] - s["start_time"]
paper = np.isin(p, lrrk2.PAPER_POWERS)
gaps = np.diff(s["start_time"])
rows.append({"mouse": s["mouse"], "line": s["line"], "group": s["group"], "epochs": len(p),
"per_power": "/".join(str((p == q).sum()) for q in sorted(set(p))),
"powers": len(set(p)), "first_8_codes": "".join(code_letter[q] for q in p[:8]),
"paper_powers_in_half": 1 if paper[:32].all() else (2 if paper[32:].all() else 0),
"train_s_paper": round(np.median(dur[paper]), 3), "pulse_ms_paper": s["pulse_length_in_ms"][paper][0],
"period_ms_paper": s["period_in_ms"][paper][0],
"train_s_other": round(np.median(dur[~paper]), 3), "pulse_ms_other": s["pulse_length_in_ms"][~paper][0],
"period_ms_other": s["period_in_ms"][~paper][0],
"min_gap_s": round(gaps.min(), 1), "median_gap_s": round(np.median(gaps), 1),
"wavelength": s["wavelength_in_nm"][0], "first_onset_s": round(s["start_time"][0], 1),
"onset_minus_series_start_s": round(s["start_time"][0] - s["t"][0], 1)})
opto = pd.DataFrame(rows).sort_values(["line", "group", "mouse"])
print("Epochs table description (first file):", sessions[0]["opto_description"])
print("\nPowers in the files (mW):", sorted(set(np.concatenate([s["power_in_mW"] for s in sessions]))))
print(opto.to_string(index=False))
print("\nTrain types carrying the paper's four powers, by line and genotype:")
print(opto.groupby(["line", "group", "train_s_paper", "pulse_ms_paper", "period_ms_paper"]).size().to_string())
odd = [(s["mouse"], j) for s in sessions for j in np.where((s["pulse_length_in_ms"] == 0) | np.isnan(s["period_in_ms"]))[0]]
print("\nEpochs with a pulse length of 0 or no period (mouse, epoch, power, length in s, pulses):",
[(m, int(j), by_mouse[m]["power_in_mW"][j], round(by_mouse[m]["stop_time"][j] - by_mouse[m]["start_time"][j], 2),
int(by_mouse[m]["number_pulses_per_pulse_train"][j])) for m, j in odd])
print("Shortest gap between onsets in any file (s):", opto.min_gap_s.min(), "(Methods: at least 20 s)")
gap_all = np.concatenate([np.diff(s["start_time"]) for s in sessions])
print("Gaps between onsets over all files (s): 5th, 50th, 95th percentiles", np.percentile(gap_all, [5, 50, 95]).round(1),
"; fraction under 20 s:", round(float((gap_all < 20).mean()), 3))
Epochs table description (first file): Epochs of optogenetic activation of ChRmine-expressing Cre+ SNc dopamine neurons (635 nm LED, right hemisphere). Each row is one contiguous stimulation epoch (TTL=1 block) extracted from the 100 Hz binned TTL column in *_data.mat. Pulse parameters: 8 ms on / 8 ms off cycles for 500 ms per train; trains spaced at least 20 s apart; 8 repetitions per power level (0.1, 0.5, 1.0, 4.0 mW, pseudorandom order). Power and pulse parameters are passed per-session via conversion_options once confirmed.
Powers in the files (mW): [np.float64(0.1), np.float64(0.25), np.float64(0.5), np.float64(1.0), np.float64(1.5), np.float64(2.0), np.float64(3.0), np.float64(4.0)]
mouse line group epochs per_power powers first_8_codes paper_powers_in_half train_s_paper pulse_ms_paper period_ms_paper train_s_other pulse_ms_other period_ms_other min_gap_s median_gap_s wavelength first_onset_s onset_minus_series_start_s
3742 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.33 9.0 10.0 0.51 8.0 16.0 15.3 15.5 635.0 164.6 162.7
3743 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 156.0 153.5
4004 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 57.1 32.4
4005 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 56.3 30.4
4007 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 51.8 30.7
4651 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 47.7 23.0
4652 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 0.0 NaN 0.33 9.0 10.0 15.3 15.5 635.0 52.7 30.1
4885 Anxa1 GS 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.33 9.0 10.0 0.51 8.0 16.0 15.3 15.5 635.0 157.6 155.3
3947 Anxa1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 155.6 153.4
3948 Anxa1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 159.8 157.9
4253 Anxa1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 60.9 30.9
4398 Anxa1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 61.0 32.7
4401 Anxa1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 54.9 29.2
4558 Anxa1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 55.9 29.5
4561 Anxa1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.33 9.0 10.0 15.3 15.5 635.0 132.4 105.1
302 Calb1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 55.3 30.5
306 Calb1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 44.5 24.9
740 Calb1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 0.0 NaN 0.51 8.0 16.0 15.5 15.5 635.0 46.9 22.5
741 Calb1 GS 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 40.7 37.8
848 Calb1 GS 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 43.8 23.4
852 Calb1 GS 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 36.4 32.5
935 Calb1 GS 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 76.2 32.0
671 Calb1 WT 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 36.6 33.4
673 Calb1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 49.0 23.5
688 Calb1 WT 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 30.2 27.5
966 Calb1 WT 64 8/8/8/8/8/8/8/8 8 ffggbbee 2 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 59.2 30.6
1803 Calb1 WT 64 8/8/8/8/8/8/8/8 8 ddaacchh 1 0.51 8.0 16.0 0.51 8.0 16.0 15.5 15.5 635.0 28.6 25.9
Train types carrying the paper's four powers, by line and genotype:
line group train_s_paper pulse_ms_paper period_ms_paper
Anxa1 GS 0.33 9.0 10.0 2
0.51 8.0 16.0 5
WT 0.51 8.0 16.0 7
Calb1 GS 0.51 8.0 16.0 6
WT 0.51 8.0 16.0 5
Epochs with a pulse length of 0 or no period (mouse, epoch, power, length in s, pulses): [(4652, 32, np.float64(1.0), np.float64(0.51), 1), (740, 32, np.float64(1.0), np.float64(0.51), 1)]
Shortest gap between onsets in any file (s): 15.3 (Methods: at least 20 s)
Gaps between onsets over all files (s): 5th, 50th, 95th percentiles [15.3 15.5 26.4] ; fraction under 20 s: 0.508
Every file holds 64 trains, 8 at each of 8 powers from 0.1 to 4 mW; the paper uses four of them (0.1, 0.5, 1 and 4 mW).
The table's own description gives 8 ms on and 8 ms off for 500 ms and only those four powers, but the table holds two
kinds of train. In Anxa1 files one half of the session has 9 ms pulses every 10 ms for 0.33 s, the other 8 ms pulses
every 16 ms for 0.51 s; Calb1 files hold only the second kind. The paper's four powers fall on 0.51 s trains in 25
files and on 0.33 s trains in two Anxa1 GS mice (3742 and 4885), so these two mice were stimulated differently from
every WT mouse; a variant of fig6e-anxa1-evoked-reduced leaves them out. One train each in mice 4652 and 740 has a
pulse length of 0, no period and one pulse, though it lasts 0.51 s like its neighbours, so its pulse columns are
wrong. Trains of the same power come in pairs 15.3 to 15.5 s apart, and 51% of the gaps are under 20 s, where the
Methods and the table description say at least 20 s.
rows = []
for s in sessions:
shifted = dict(s, onset=s["onset"] + 1000, offset=s["offset"] + 1000) # 10 s later, between trains
true = lrrk2.evoked_integrals(s, "fixed")
moved = lrrk2.evoked_integrals(shifted, "fixed")
iso = lrrk2.evoked_integrals(s, "fixed", trace="dff405")
traces = np.array([s["dff"][i - 100:i + 300] for i in s["onset"] if i + 300 <= len(s["dff"])])
mean_trace = traces.mean(0)
ok = ~np.isnan(true)
rows.append({"mouse": s["mouse"], "line": s["line"], "group": s["group"],
"median_at_onsets": np.nanmedian(true), "median_10s_later": np.nanmedian(moved),
"median_405_at_onsets": np.nanmedian(iso),
"peak_latency_ms": (np.argmax(mean_trace) - 100) * 10,
"rise_before_onset": mean_trace[90:100].mean() - mean_trace[:90].mean(),
"spearman_power": stats.spearmanr(s["power_in_mW"][ok], true[ok])[0],
**{f"{q}mW": np.nanmedian(true[s["power_in_mW"] == q]) for q in sorted(set(s["power_in_mW"]))}})
clock = pd.DataFrame(rows).sort_values(["line", "group", "mouse"])
print("Integral from 0 to 0.5 s after onset minus the mean of -1.0 to -0.1 s (% dF/F x s), median over each file's 64",
"trains, at the table's onsets, 10 s later, and for the 405 nm control; latency of the peak of the mean trace;",
"Spearman correlation of the integral with the labelled power; median integral per labelled power.")
print(clock.round(2).to_string(index=False))
print("\nFiles where the median at onsets exceeds the median 10 s later:", int((clock.median_at_onsets > clock.median_10s_later).sum()),
"of", len(clock), "; Wilcoxon p =", f"{stats.wilcoxon(clock.median_at_onsets, clock.median_10s_later).pvalue:.2g}")
print("Peak latency (ms) from", clock.peak_latency_ms.min(), "to", clock.peak_latency_ms.max(),
"; mean rise in the 0.1 s before onset from", clock.rise_before_onset.min().round(2), "to", clock.rise_before_onset.max().round(2))
print("Spearman correlation with power, median by line:", clock.groupby("line").spearman_power.median().round(2).to_dict())
# The two Anxa1 G2019S mice whose paper powers are 0.33 s trains, against two GS mice given 0.51 s trains,
# one epoch per row: labelled power, train type and the 0 to 0.5 s integral.
rows = []
for m in (3742, 4885, 4004, 4651):
s = by_mouse[m]
v = lrrk2.evoked_integrals(s, "fixed")
for j in range(len(v)):
rows.append({"mouse": m, "epoch": j, "power": s["power_in_mW"][j],
"train_s": round(s["stop_time"][j] - s["start_time"][j], 2),
"train": "0.33 s, 9/10 ms" if s["period_in_ms"][j] == 10 else "0.51 s, 8/16 ms", "pulse_ms": s["pulse_length_in_ms"][j],
"period_ms": s["period_in_ms"][j], "integral_0_0.5s": v[j]})
ep = pd.DataFrame(rows)
print("\nEpoch by epoch, mice 3742 and 4885 (0.33 s trains at the paper's powers) and 4004 and 4651 (0.51 s trains):")
for m in (3742, 4885):
print(ep[ep.mouse == m].round(2).to_string(index=False))
print("\nMedian 0 to 0.5 s integral by labelled power and train type:")
print(ep.pivot_table(index=["mouse", "train"], columns="power", values="integral_0_0.5s", aggfunc="median").round(1).to_string())
print("Spearman correlation of the integral with labelled power, within each train type:")
print(ep.groupby(["mouse", "train"]).apply(lambda g: round(stats.spearmanr(g.power, g["integral_0_0.5s"], nan_policy="omit")[0], 2)).to_string())
# Anxa1 files where 1.5, 2 and 3 mW are 0.33 s trains and 0.5 and 1 mW are 0.51 s trains: do the former fall below?
rows = []
for s in sessions:
if s["line"] != "Anxa1":
continue
v = lrrk2.evoked_integrals(s, "fixed")
kind = {q: set(s["period_in_ms"][s["power_in_mW"] == q]) for q in (0.5, 1.0, 1.5, 2.0, 3.0)}
if not (all(kind[q] == {10} for q in (1.5, 2.0, 3.0)) and all(kind[q] == {16} for q in (0.5, 1.0))):
continue
med = {q: np.nanmedian(v[s["power_in_mW"] == q]) for q in kind}
rows.append({"mouse": s["mouse"], "group": s["group"], **{f"{q}mW": med[q] for q in kind},
"high_033_below_low_051": max(med[q] for q in (1.5, 2.0, 3.0)) < min(med[q] for q in (0.5, 1.0))})
split = pd.DataFrame(rows)
print("\nAnxa1 files with 1.5, 2 and 3 mW as 0.33 s trains and 0.5 and 1 mW as 0.51 s trains, median 0 to 0.5 s integral:")
print(split.round(1).to_string(index=False))
print("Files where every 0.33 s median (1.5, 2, 3 mW) is below both 0.51 s medians (0.5, 1 mW):",
int(split.high_033_below_low_051.sum()), "of", len(split))
Integral from 0 to 0.5 s after onset minus the mean of -1.0 to -0.1 s (% dF/F x s), median over each file's 64 trains, at the table's onsets, 10 s later, and for the 405 nm control; latency of the peak of the mean trace; Spearman correlation of the integral with the labelled power; median integral per labelled power.
mouse line group median_at_onsets median_10s_later median_405_at_onsets peak_latency_ms rise_before_onset spearman_power 0.1mW 0.25mW 0.5mW 1.0mW 1.5mW 2.0mW 3.0mW 4.0mW
3742 Anxa1 GS 18.38 -1.83 0.04 270 1.19 -0.13 33.55 9.39 5.68 31.38 37.38 16.49 3.02 24.14
3743 Anxa1 GS 128.50 -1.18 -0.39 210 12.37 0.72 51.11 95.26 123.81 161.17 131.19 127.63 132.15 182.33
4004 Anxa1 GS 21.36 0.36 -0.07 160 4.88 0.12 19.28 21.72 25.18 25.32 18.11 20.53 18.57 33.34
4005 Anxa1 GS 13.02 0.06 -0.09 160 1.98 0.00 8.61 19.88 14.16 10.23 14.97 12.79 13.16 12.68
4007 Anxa1 GS 25.84 -0.20 -0.15 160 4.89 -0.18 23.60 28.41 32.78 32.62 21.19 21.70 22.53 29.74
4651 Anxa1 GS 60.88 1.34 -0.13 210 7.48 0.22 54.32 60.08 81.27 91.54 54.21 55.10 57.68 100.06
4652 Anxa1 GS 81.15 1.12 -0.10 300 6.40 0.92 26.57 54.59 63.91 77.19 86.24 93.81 94.81 113.12
4885 Anxa1 GS 54.67 0.13 -0.10 170 10.23 -0.06 40.57 78.08 53.51 35.02 66.89 69.51 56.14 38.70
3947 Anxa1 WT 85.90 3.36 -0.28 240 7.96 -0.00 78.26 90.32 128.36 114.78 67.25 55.41 66.15 147.72
3948 Anxa1 WT 148.32 4.18 -0.28 220 13.59 0.93 48.97 97.94 107.85 146.71 164.83 160.72 175.54 226.16
4253 Anxa1 WT 105.20 1.87 -0.36 170 17.09 -0.06 92.65 108.74 132.98 132.93 84.00 93.88 87.39 116.49
4398 Anxa1 WT 160.94 1.44 -0.74 240 13.86 0.93 53.89 100.69 114.76 154.88 173.51 169.51 172.83 213.69
4401 Anxa1 WT 142.27 2.97 -0.47 180 25.09 0.04 128.19 143.90 179.98 178.24 124.72 133.06 127.32 191.01
4558 Anxa1 WT 127.92 0.37 -0.28 210 12.90 0.74 47.78 100.63 121.80 157.36 138.08 135.11 125.24 201.76
4561 Anxa1 WT 63.13 -0.10 -0.21 190 6.38 0.06 48.78 68.68 88.22 85.72 53.81 50.36 56.03 80.23
302 Calb1 GS 120.12 1.99 -0.30 270 9.26 0.81 6.88 58.00 69.64 117.53 163.89 194.27 194.75 155.76
306 Calb1 GS 101.63 -0.64 0.14 370 11.54 0.82 31.31 72.94 57.33 98.80 121.57 130.13 147.55 121.59
740 Calb1 GS 43.15 0.13 -0.03 300 -1.76 0.76 1.15 15.19 18.21 51.89 54.91 55.81 56.14 51.26
741 Calb1 GS 125.63 1.12 0.32 260 14.59 0.92 30.77 59.04 88.62 106.25 138.77 146.06 149.00 195.22
848 Calb1 GS 94.21 -0.32 0.26 270 9.87 0.83 27.28 65.90 55.67 80.39 122.03 132.86 142.60 121.47
852 Calb1 GS 152.43 5.41 0.44 270 10.40 0.94 40.52 64.97 99.54 134.82 170.12 200.81 205.92 205.64
935 Calb1 GS 129.49 3.74 0.16 220 11.38 0.86 29.22 62.85 89.60 107.65 144.98 154.00 161.58 148.11
671 Calb1 WT 198.16 0.47 0.45 250 11.64 0.89 52.97 84.89 147.90 190.68 205.06 236.03 226.55 274.11
673 Calb1 WT 62.52 0.72 0.01 190 7.88 0.84 17.00 29.76 49.80 63.01 71.44 75.42 71.74 81.01
688 Calb1 WT 216.36 1.35 0.42 270 16.85 0.86 79.03 141.89 174.67 196.44 243.43 239.12 268.27 252.57
966 Calb1 WT 128.71 1.07 -0.06 280 8.94 0.87 29.82 50.19 89.86 103.25 156.80 175.12 189.85 161.63
1803 Calb1 WT 260.39 2.06 1.15 300 16.37 0.94 86.68 140.64 185.63 279.83 254.11 309.31 324.59 441.64
Files where the median at onsets exceeds the median 10 s later: 27 of 27 ; Wilcoxon p = 1.5e-08
Peak latency (ms) from 160 to 370 ; mean rise in the 0.1 s before onset from -1.76 to 25.09
Spearman correlation with power, median by line: {'Anxa1': 0.06, 'Calb1': 0.86}
Epoch by epoch, mice 3742 and 4885 (0.33 s trains at the paper's powers) and 4004 and 4651 (0.51 s trains):
mouse epoch power train_s train pulse_ms period_ms integral_0_0.5s
3742 0 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 26.90
3742 1 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 16.25
3742 2 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 41.78
3742 3 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 30.36
3742 4 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 -6.62
3742 5 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 -1.82
3742 6 4.00 0.32 0.33 s, 9/10 ms 9.0 10.0 25.51
3742 7 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 36.87
3742 8 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 34.88
3742 9 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 33.69
3742 10 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 32.30
3742 11 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 46.10
3742 12 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 8.88
3742 13 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 7.39
3742 14 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 22.77
3742 15 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 17.19
3742 16 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 32.57
3742 17 1.00 0.32 0.33 s, 9/10 ms 9.0 10.0 30.19
3742 18 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 36.32
3742 19 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 23.58
3742 20 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 18.79
3742 21 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 3.97
3742 22 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 22.02
3742 23 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 32.59
3742 24 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 29.90
3742 25 1.00 0.32 0.33 s, 9/10 ms 9.0 10.0 45.06
3742 26 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 34.79
3742 27 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 25.79
3742 28 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 17.21
3742 29 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 3.50
3742 30 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 28.30
3742 31 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 21.37
3742 32 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 -1.09
3742 33 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 19.65
3742 34 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 4.96
3742 35 3.00 0.52 0.51 s, 8/16 ms 8.0 16.0 5.52
3742 36 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 17.56
3742 37 0.25 0.52 0.51 s, 8/16 ms 8.0 16.0 14.60
3742 38 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 37.69
3742 39 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 37.41
3742 40 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 16.65
3742 41 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 16.34
3742 42 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 2.55
3742 43 3.00 0.52 0.51 s, 8/16 ms 8.0 16.0 -4.95
3742 44 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 8.40
3742 45 0.25 0.52 0.51 s, 8/16 ms 8.0 16.0 7.08
3742 46 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 39.82
3742 47 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 50.44
3742 48 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 17.96
3742 49 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 16.07
3742 50 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 6.34
3742 51 3.00 0.52 0.51 s, 8/16 ms 8.0 16.0 -12.81
3742 52 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 10.38
3742 53 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 0.73
3742 54 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 37.36
3742 55 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 33.28
3742 56 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 4.26
3742 57 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 17.54
3742 58 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 0.23
3742 59 3.00 0.52 0.51 s, 8/16 ms 8.0 16.0 3.50
3742 60 0.25 0.52 0.51 s, 8/16 ms 8.0 16.0 12.22
3742 61 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 1.90
3742 62 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 22.45
3742 63 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 34.31
mouse epoch power train_s train pulse_ms period_ms integral_0_0.5s
4885 0 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 29.60
4885 1 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 34.24
4885 2 0.10 0.32 0.33 s, 9/10 ms 9.0 10.0 29.79
4885 3 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 29.80
4885 4 0.50 0.32 0.33 s, 9/10 ms 9.0 10.0 54.57
4885 5 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 49.31
4885 6 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 27.60
4885 7 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 40.34
4885 8 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 23.36
4885 9 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 34.99
4885 10 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 35.71
4885 11 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 40.88
4885 12 0.50 0.32 0.33 s, 9/10 ms 9.0 10.0 53.44
4885 13 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 43.95
4885 14 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 31.52
4885 15 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 39.29
4885 16 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 35.05
4885 17 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 37.33
4885 18 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 40.62
4885 19 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 40.52
4885 20 0.50 0.32 0.33 s, 9/10 ms 9.0 10.0 59.70
4885 21 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 52.88
4885 22 4.00 0.32 0.33 s, 9/10 ms 9.0 10.0 38.12
4885 23 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 42.08
4885 24 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 46.82
4885 25 1.00 0.33 0.33 s, 9/10 ms 9.0 10.0 43.92
4885 26 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 46.27
4885 27 0.10 0.33 0.33 s, 9/10 ms 9.0 10.0 41.19
4885 28 0.50 0.32 0.33 s, 9/10 ms 9.0 10.0 66.23
4885 29 0.50 0.33 0.33 s, 9/10 ms 9.0 10.0 53.57
4885 30 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 41.25
4885 31 4.00 0.33 0.33 s, 9/10 ms 9.0 10.0 34.39
4885 32 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 63.10
4885 33 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 69.58
4885 34 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 50.41
4885 35 3.00 0.52 0.51 s, 8/16 ms 8.0 16.0 53.88
4885 36 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 74.75
4885 37 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 75.64
4885 38 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 60.71
4885 39 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 67.33
4885 40 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 64.54
4885 41 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 66.61
4885 42 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 57.50
4885 43 3.00 0.52 0.51 s, 8/16 ms 8.0 16.0 54.78
4885 44 0.25 0.52 0.51 s, 8/16 ms 8.0 16.0 82.85
4885 45 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 80.42
4885 46 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 60.31
4885 47 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 58.28
4885 48 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 73.73
4885 49 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 69.43
4885 50 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 61.81
4885 51 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 54.79
4885 52 0.25 0.52 0.51 s, 8/16 ms 8.0 16.0 67.15
4885 53 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 76.44
4885 54 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 67.40
4885 55 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 66.45
4885 56 2.00 0.51 0.51 s, 8/16 ms 8.0 16.0 74.78
4885 57 2.00 0.52 0.51 s, 8/16 ms 8.0 16.0 74.20
4885 58 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 62.37
4885 59 3.00 0.51 0.51 s, 8/16 ms 8.0 16.0 60.14
4885 60 0.25 0.52 0.51 s, 8/16 ms 8.0 16.0 81.20
4885 61 0.25 0.51 0.51 s, 8/16 ms 8.0 16.0 79.71
4885 62 1.50 0.51 0.51 s, 8/16 ms 8.0 16.0 78.64
4885 63 1.50 0.52 0.51 s, 8/16 ms 8.0 16.0 71.16
Median 0 to 0.5 s integral by labelled power and train type:
power 0.10 0.25 0.50 1.00 1.50 2.00 3.00 4.00
mouse train
3742 0.33 s, 9/10 ms 33.5 NaN 5.7 31.4 NaN NaN NaN 24.1
0.51 s, 8/16 ms NaN 9.4 NaN NaN 37.4 16.5 3.0 NaN
4004 0.33 s, 9/10 ms NaN 21.7 NaN NaN 18.1 20.5 18.6 NaN
0.51 s, 8/16 ms 19.3 NaN 25.2 25.3 NaN NaN NaN 33.3
4651 0.33 s, 9/10 ms NaN 60.1 NaN NaN 54.2 55.1 57.7 NaN
0.51 s, 8/16 ms 54.3 NaN 81.3 91.5 NaN NaN NaN 100.1
4885 0.33 s, 9/10 ms 40.6 NaN 53.5 35.0 NaN NaN NaN 38.7
0.51 s, 8/16 ms NaN 78.1 NaN NaN 66.9 69.5 56.1 NaN
Spearman correlation of the integral with labelled power, within each train type:
mouse train
3742 0.33 s, 9/10 ms -0.07
0.51 s, 8/16 ms -0.38
4004 0.33 s, 9/10 ms -0.41
0.51 s, 8/16 ms 0.81
4651 0.33 s, 9/10 ms -0.21
0.51 s, 8/16 ms 0.86
4885 0.33 s, 9/10 ms -0.28
0.51 s, 8/16 ms -0.74
Anxa1 files with 1.5, 2 and 3 mW as 0.33 s trains and 0.5 and 1 mW as 0.51 s trains, median 0 to 0.5 s integral:
mouse group 0.5mW 1.0mW 1.5mW 2.0mW 3.0mW high_033_below_low_051
3743 GS 123.8 161.2 131.2 127.6 132.2 False
3947 WT 128.4 114.8 67.2 55.4 66.2 True
3948 WT 107.8 146.7 164.8 160.7 175.5 False
4004 GS 25.2 25.3 18.1 20.5 18.6 True
4005 GS 14.2 10.2 15.0 12.8 13.2 False
4007 GS 32.8 32.6 21.2 21.7 22.5 True
4253 WT 133.0 132.9 84.0 93.9 87.4 True
4398 WT 114.8 154.9 173.5 169.5 172.8 False
4401 WT 180.0 178.2 124.7 133.1 127.3 True
4558 WT 121.8 157.4 138.1 135.1 125.2 False
4561 WT 88.2 85.7 53.8 50.4 56.0 True
4651 GS 81.3 91.5 54.2 55.1 57.7 True
Files where every 0.33 s median (1.5, 2, 3 mW) is below both 0.51 s medians (0.5, 1 mW): 7 of 12
At the table's onsets the 0 to 0.5 s integral has a median of 13 to 260 % dF/F x s per file; 10 s later, between trains, it is near zero in every file (lower in 27 of 27, Wilcoxon p = 1.5e-08), and the 405 nm control shows no response (medians -0.74 to 1.15). The mean response peaks 160 to 370 ms after onset. The mean trace rises by up to 25 % dF/F in the 0.1 s before onset: the 20-sample moving mean of the stored dF/F spreads the response about 0.1 s earlier, which is why the code's baseline ends 0.1 s before onset. The epochs table and the photometry share a clock.
The response grows with the labelled power in every Calb1 file (Spearman 0.76 to 0.94) but not in many Anxa1 files
(median 0.06). Of the 12 Anxa1 files where 1.5, 2 and 3 mW are given as 0.33 s trains and 0.5 and 1 mW as 0.51 s
trains, the 0.33 s responses fall below both 0.51 s ones in 7 (for example mouse 4401) and not in the other 5 (in 4398,
173.5 at 1.5 mW against 114.8 at 0.5 mW). The power labels are consistent with the data in Calb1
files; in Anxa1 files the data alone cannot confirm them. The labels of the four powers the paper uses are confirmed
against its source data under source-data-link.
The epoch-by-epoch tables show mice 3742 and 4885, whose paper powers are all 0.33 s trains of 9 ms pulses every 10 ms, and two GS mice given 0.51 s trains. In 3742 the response does not follow the labelled power: within the 0.33 s trains the median 0 to 0.5 s integral is lowest at 0.5 mW (5.7 % dF/F x s) and higher at 0.1 mW (33.5), and the correlation with power is negative within each train type. In 4885 the 0.33 s trains give medians of 35.0 to 53.5 at the four labelled powers, below every 0.51 s power (56.1 to 78.1), and the correlation with power is negative within both train types (-0.28 and -0.74). In 4004 and 4651 the response rises with power within the 0.51 s trains (Spearman 0.81 and 0.86) and not within the 0.33 s ones (-0.41 and -0.21). The data cannot confirm the power labels of 3742 and 4885.
Claims¶
source-data-link¶
The dandiset's recordings are those behind the paper's Figure 6 and Supplementary Figures 7 and 8, and the paper's published per-stimulation traces, evoked integrals and spontaneous transient integrals can be recomputed from them.
Records are matched by mouse ID, group, labelled power and the rank of the stimulation within its power. The source data file is read in memory from Zenodo.
md5, src = lrrk2.load_source_data()
print("Source data file md5:", md5, "; matches the Zenodo record:", md5 == lrrk2.SOURCE_DATA_MD5)
print("Records per table:", {k: len(v) for k, v in src.items()})
line_of = {"6C": "Anxa1", "6E": "Anxa1", "6F": "Anxa1", "S7B": "Anxa1", "S7C": "Anxa1",
"S8C": "Calb1", "S8D": "Calb1", "S8E": "Calb1"}
# (1) Mice
mice = {(k, d["mouse"], d["group"]) for k, v in src.items() for d in v}
part1 = pd.DataFrame([{"table": k, "mouse": m, "group": g, "in_dandiset": m in by_mouse,
"same_group": m in by_mouse and by_mouse[m]["group"] == g,
"same_line": m in by_mouse and by_mouse[m]["line"] == line_of[k]} for k, m, g in mice])
part1["match"] = part1.in_dandiset & part1.same_group & part1.same_line
mouse_level = part1.groupby("mouse").match.all()
print(f"\n(1) Mice: {int(mouse_level.sum())} of {len(mouse_level)} source-data mice are in the dandiset with the same genotype and line;",
"dandiset mice not in the source data:", sorted(set(by_mouse) - set(mouse_level.index)))
def rank_match(records):
"""Pair each source record with the dandiset epoch of the same mouse and power and the same rank."""
seen, out = {}, []
for d in records:
key = (d["mouse"], d["power"])
r = seen.get(key, 0)
seen[key] = r + 1
s = by_mouse[d["mouse"]]
idx = np.where(np.isclose(s["power_in_mW"], d["power"]))[0]
out.append((d, s, idx[r], idx))
return out
# (2) Triggered traces
rows = []
for key in ("6C", "S7B", "S8D"):
for d, s, j, idx in rank_match(src[key]):
ours = lrrk2.code_chunk(s, j)
rows.append({"table": key, "line": s["line"], "mouse": d["mouse"], "power": d["power"],
"max_diff": np.nanmax(np.abs(ours - d["trace"])), "nan_here": int(np.isnan(ours).sum()),
"r": np.corrcoef(ours[~np.isnan(ours)], d["trace"][~np.isnan(ours)])[0, 1],
"slope": np.polyfit(ours[~np.isnan(ours)], d["trace"][~np.isnan(ours)], 1)[0],
"offset": np.polyfit(ours[~np.isnan(ours)], d["trace"][~np.isnan(ours)], 1)[1]})
part2 = pd.DataFrame(rows)
part2["match"] = part2.max_diff < 1e-6
print("\n(2) Triggered traces equal to the dandiset's dF/F from 1 s before the onset of the epoch with the same power and rank:")
print(part2.groupby("table").agg(records=("match", "size"), matched=("match", "sum"), largest_diff=("max_diff", "max"),
median_r=("r", "median"), slope_min=("slope", "min"), slope_max=("slope", "max")).to_string())
print("Largest difference among matched traces, per table:", {k: f"{v:.2g}" for k, v in part2[part2.match].groupby("table").max_diff.max().items()})
print("Unmatched Anxa1 traces:")
print(part2[(part2.line == "Anxa1") & ~part2.match].to_string(index=False))
m4398 = by_mouse[4398]
print(f"Mouse 4398: last onset at {m4398['start_time'][-1]:.2f} s, processed series ends at {m4398['t'][-1]:.2f} s")
# (3) Evoked integrals
code_auc = {s["mouse"]: lrrk2.evoked_integrals(s, "code") * lrrk2.FS for s in sessions} # in samples, as the code
rows = []
for key in ("6E", "S7C", "S8E"):
for d, s, j, idx in rank_match(src[key]):
four = np.where(np.isclose(s["power_in_mW"], 4.0))[0][list(idx).index(j)]
rows.append({"table": key, "line": s["line"], "mouse": d["mouse"], "power": d["power"], "published": d["value"],
"ours": code_auc[d["mouse"]][j], "ours_4mW_same_rank": code_auc[d["mouse"]][four]})
part3 = pd.DataFrame(rows)
factor = float(np.nanmedian(part3.published / part3.ours))
part3["match"] = np.abs(part3.published - factor * part3.ours) <= 1e-4 * np.abs(part3.published)
part3["match_if_4mW"] = np.abs(part3.published - factor * part3.ours_4mW_same_rank) <= 1e-4 * np.abs(part3.published)
print(f"\n(3) Evoked integrals: common factor published / ours (code's window, in samples) = {factor:.6g}")
print(part3.groupby(["table", "power"]).agg(records=("match", "size"), matched=("match", "sum"),
matched_if_4mW=("match_if_4mW", "sum")).to_string())
print("Calb1 records: ratio published / ours from", part3[part3.line == "Calb1"].eval("published / ours").min().round(6),
"to", part3[part3.line == "Calb1"].eval("published / ours").max().round(6))
print("Unmatched Anxa1 records:", part3[(part3.line == "Anxa1") & ~part3.match][["table", "mouse", "power", "published", "ours"]].to_dict("records"))
# (4) Spontaneous transients
measures = {ex: {s["mouse"]: lrrk2.spontaneous_transients(s, ex) for s in sessions} for ex in ("methods", "code_as_written")}
rows = []
for key in ("6F", "S8C"):
pub = pd.DataFrame(src[key])
for m, g in pub.groupby("mouse", sort=False):
row = {"table": key, "line": line_of[key], "mouse": m, "published": len(g)}
for ex in ("methods", "code_as_written"):
auc, peaks, means = measures[ex][m]
row[f"n_{ex}"] = len(auc)
for name, v in (("integral", auc), ("mean", means)):
ratio = g.value.values / v if len(v) == len(g) else np.array([np.nan])
row[f"{name}_ratio_{ex}"] = np.median(ratio)
row[f"{name}_ratio_cv_{ex}"] = np.std(ratio) / np.mean(ratio)
rows.append(row)
part4 = pd.DataFrame(rows)
for ex in ("methods", "code_as_written"):
for name in ("integral", "mean"):
f4 = np.nanmedian(part4[f"{name}_ratio_{ex}"])
for rtol, tag in ((1e-2, ""), (1e-4, "_1e-4")):
ok = []
for _, r in part4.iterrows():
pub = pd.DataFrame(src[r.table]).query("mouse == @r.mouse").value.values
v = measures[ex][r.mouse][0 if name == "integral" else 2]
ok.append(len(pub) == len(v) and bool(np.allclose(np.sort(pub), f4 * np.sort(v), rtol=rtol, atol=0)))
part4[f"match_{name}_{ex}{tag}"] = ok
print(f"(4) exclusion '{ex}', {name} of each transient: common factor {f4:.6g}; mice whose count and sorted values match",
f"within a relative {rtol:g}:", int(np.sum(ok)), "of", len(ok), "(Anxa1", int(np.sum(np.array(ok)[part4.line == 'Anxa1'])), "of 15)")
cols = ["table", "mouse", "published", "n_methods", "n_code_as_written", "integral_ratio_cv_methods", "mean_ratio_methods",
"mean_ratio_cv_methods", "match_integral_methods", "match_mean_methods", "match_mean_methods_1e-4",
"match_mean_code_as_written"]
print(part4[cols].to_string(index=False, float_format=lambda v: f"{v:.4g}"))
for _, r in part4[(part4.line == "Anxa1") & ~part4.match_mean_methods].iterrows():
pub = np.sort(pd.DataFrame(src[r.table]).query("mouse == @r.mouse").value.values)
v = np.sort(measures["methods"][r.mouse][2]) * float(np.nanmedian(part4.mean_ratio_methods))
rel = np.abs(pub - v) / np.abs(pub)
print(f"Anxa1 mouse {r.mouse} fails the 1% match on the mean: {int((rel > 1e-2).sum())} of {len(rel)} sorted values differ by more",
f"than 1% (largest {rel.max():.3g}); its own ratio {r.mean_ratio_methods:.6g}, CV {r.mean_ratio_cv_methods:.3g}")
print("Transients: published", part4.published.sum(), "; here, Methods exclusion", part4.n_methods.sum(),
"; code's exclusion as written", part4.n_code_as_written.sum())
print("Count equal to the published count, Methods exclusion:", part4.groupby("line").apply(lambda g: f"{int((g.published == g.n_methods).sum())} of {len(g)}").to_dict())
score = {"mice": float(mouse_level.mean()), "traces": float(part2.match.mean()), "evoked integrals": float(part3.match.mean()),
"spontaneous integrals (best window)": float(max(part4.match_integral_methods.mean(), part4.match_integral_code_as_written.mean()))}
print("Part (4) with integrals at a relative 1e-4, the tolerance of part (3):",
round(float(max(part4["match_integral_methods_1e-4"].mean(), part4["match_integral_code_as_written_1e-4"].mean())), 3))
print("\nFraction of records that match, per part, as the criterion scores them:", {k: round(v, 3) for k, v in score.items()})
print("Variant, part (4) with the mean dF/F of each transient, within 1%:",
round(float(max(part4.match_mean_methods.mean(), part4.match_mean_code_as_written.mean())), 3), "; within 1e-4:",
round(float(max(part4["match_mean_methods_1e-4"].mean(), part4["match_mean_code_as_written_1e-4"].mean())), 3))
# (6) The paper's statistics recomputed from its own published values, to check the source data against the text.
pubdf = {k: pd.DataFrame(src[k]) for k in ("6E", "6F", "S7C", "S8C", "S8E")}
rows = []
for key, power, paper_p in (("6E", 0.5, "1.10e-20"), ("S7C", 4.0, "2.39e-16"), ("S8E", 0.5, "0.0002"), ("S8E", 4.0, "0.001")):
g = pubdf[key][pubdf[key].power == power]
gs, wt = g[g.group == "GS"].value, g[g.group == "WT"].value
rows.append({"panel": key, "power": power, "n_GS": len(gs), "n_WT": len(wt), "p_one_tailed": lrrk2.one_tailed_less(gs, wt)[1],
"paper_p": paper_p, "cohens_d": lrrk2.cohens_d(wt, gs)})
for key, paper_p in (("6F", "2.75e-177"), ("S8C", "0.98")):
g = pubdf[key]
gs, wt = g[g.group == "GS"].value, g[g.group == "WT"].value
rows.append({"panel": key, "power": np.nan, "n_GS": len(gs), "n_WT": len(wt), "p_one_tailed": lrrk2.one_tailed_less(gs, wt)[1],
"paper_p": paper_p, "cohens_d": lrrk2.cohens_d(wt, gs)})
print("\n(6) Statistics from the published values themselves (paper's Cohen's d at 0.5 mW: Anxa1 2.05, Calb1 0.76):")
print(pd.DataFrame(rows).to_string(index=False, float_format=lambda v: f"{v:.3g}"))
Source data file md5: c26fe3de8f340a3db5fa29a40e5a8c10 ; matches the Zenodo record: True
Records per table: {'6C': 120, '6E': 120, '6F': 1862, 'S7B': 480, 'S7C': 480, 'S8C': 1652, 'S8D': 384, 'S8E': 384}
(1) Mice: 27 of 27 source-data mice are in the dandiset with the same genotype and line; dandiset mice not in the source data: []
(2) Triggered traces equal to the dandiset's dF/F from 1 s before the onset of the epoch with the same power and rank:
records matched largest_diff median_r slope_min slope_max
table
6C 120 120 5.684342e-13 1.000000 1.000000 1.000000
S7B 480 479 1.098330e+02 1.000000 1.000000 1.043088
S8D 384 0 1.049231e+03 0.943889 -0.882173 1.552810
Largest difference among matched traces, per table: {'6C': '5.7e-13', 'S7B': '5.7e-13'}
Unmatched Anxa1 traces:
table line mouse power max_diff nan_here r slope offset match
S7B Anxa1 4398 4.0 109.833047 68 0.992197 1.043088 34.343536 False
Mouse 4398: last onset at 1250.24 s, processed series ends at 1251.05 s
(3) Evoked integrals: common factor published / ours (code's window, in samples) = 0.0001
records matched matched_if_4mW
table power
6E 0.5 120 120 0
S7C 0.1 120 120 0
0.5 120 120 0
1.0 120 120 0
4.0 120 119 119
S8E 0.1 96 0 0
0.5 96 0 0
1.0 96 0 0
4.0 96 0 0
Calb1 records: ratio published / ours from -0.000849 to 0.000905
Unmatched Anxa1 records: [{'table': 'S7C', 'mouse': 4398, 'power': 4.0, 'published': 3.70650168535861, 'ours': nan}]
(4) exclusion 'methods', integral of each transient: common factor 0.000894123; mice whose count and sorted values match within a relative 0.01: 0 of 27 (Anxa1 0 of 15) (4) exclusion 'methods', integral of each transient: common factor 0.000894123; mice whose count and sorted values match within a relative 0.0001: 0 of 27 (Anxa1 0 of 15) (4) exclusion 'methods', mean of each transient: common factor 0.00499723; mice whose count and sorted values match within a relative 0.01: 13 of 27 (Anxa1 13 of 15) (4) exclusion 'methods', mean of each transient: common factor 0.00499723; mice whose count and sorted values match within a relative 0.0001: 0 of 27 (Anxa1 0 of 15) (4) exclusion 'code_as_written', integral of each transient: common factor nan; mice whose count and sorted values match within a relative 0.01: 0 of 27 (Anxa1 0 of 15)
(4) exclusion 'code_as_written', integral of each transient: common factor nan; mice whose count and sorted values match within a relative 0.0001: 0 of 27 (Anxa1 0 of 15)
(4) exclusion 'code_as_written', mean of each transient: common factor nan; mice whose count and sorted values match within a relative 0.01: 0 of 27 (Anxa1 0 of 15)
(4) exclusion 'code_as_written', mean of each transient: common factor nan; mice whose count and sorted values match within a relative 0.0001: 0 of 27 (Anxa1 0 of 15)
table mouse published n_methods n_code_as_written integral_ratio_cv_methods mean_ratio_methods mean_ratio_cv_methods match_integral_methods match_mean_methods match_mean_methods_1e-4 match_mean_code_as_written
6F 4558 93 93 39 0.9793 0.004998 0.0008238 False True False False
6F 4561 131 131 47 0.7188 0.004996 0.0006883 False True False False
6F 4398 162 162 28 0.7546 0.004997 0.001003 False False False False
6F 4401 76 76 57 0.6492 0.004999 0.0002882 False True False False
6F 3947 81 81 69 0.7144 0.004999 0.0007371 False True False False
6F 3948 151 151 56 0.8881 0.004997 0.0008199 False True False False
6F 4253 104 104 67 0.9779 0.004997 0.001583 False True False False
6F 4885 93 93 20 1.07 0.004997 0.0007055 False True False False
6F 4651 57 57 64 0.2104 0.004999 0.0001155 False True False False
6F 4652 79 79 50 0.8163 0.004999 0.001028 False True False False
6F 3742 285 285 229 0.7331 0.004994 0.001126 False True False False
6F 3743 132 132 69 0.8883 0.004998 0.0007105 False True False False
6F 4004 143 143 77 1.039 0.004995 0.002894 False True False False
6F 4005 122 122 66 0.91 0.004996 0.002073 False True False False
6F 4007 153 153 73 0.9986 0.004995 0.00191 False False False False
S8C 671 133 159 60 NaN NaN NaN False False False False
S8C 673 219 159 129 NaN NaN NaN False False False False
S8C 966 120 79 65 NaN NaN NaN False False False False
S8C 1803 118 132 9 NaN NaN NaN False False False False
S8C 688 92 106 12 NaN NaN NaN False False False False
S8C 848 141 120 73 NaN NaN NaN False False False False
S8C 852 117 165 31 NaN NaN NaN False False False False
S8C 302 122 93 55 NaN NaN NaN False False False False
S8C 306 148 75 59 NaN NaN NaN False False False False
S8C 740 179 140 132 NaN NaN NaN False False False False
S8C 741 158 158 114 0.6889 0.001398 0.3307 False False False False
S8C 935 105 145 76 NaN NaN NaN False False False False
Anxa1 mouse 4398 fails the 1% match on the mean: 1 of 162 sorted values differ by more than 1% (largest 0.0103); its own ratio 0.00499723, CV 0.001
Anxa1 mouse 4007 fails the 1% match on the mean: 1 of 153 sorted values differ by more than 1% (largest 0.0167); its own ratio 0.00499501, CV 0.00191
Transients: published 3514 ; here, Methods exclusion 3393 ; code's exclusion as written 1826
Count equal to the published count, Methods exclusion: {'Anxa1': '15 of 15', 'Calb1': '1 of 12'}
Part (4) with integrals at a relative 1e-4, the tolerance of part (3): 0.0
Fraction of records that match, per part, as the criterion scores them: {'mice': 1.0, 'traces': 0.609, 'evoked integrals': 0.609, 'spontaneous integrals (best window)': 0.0}
Variant, part (4) with the mean dF/F of each transient, within 1%: 0.481 ; within 1e-4: 0.0
(6) Statistics from the published values themselves (paper's Cohen's d at 0.5 mW: Anxa1 2.05, Calb1 0.76):
panel power n_GS n_WT p_one_tailed paper_p cohens_d
6E 0.5 64 56 1.1e-20 1.10e-20 2.06
S7C 4 64 56 2.39e-16 2.39e-16 1.72
S8E 0.5 56 40 0.000185 0.0002 0.765
S8E 4 56 40 0.00147 0.001 0.632
6F NaN 1064 798 2.75e-177 2.75e-177 1.49
S8C NaN 970 682 0.989 0.98 -0.114
To see why the Calb1 records differ, the dF/F is recomputed from the raw 470 nm signal in the dandiset with the code's binning and baseline correction, at the code's background fraction (0.85 of the sliding 8th percentile) and at other fractions, and compared with the published triggered traces. The edges of the sliding percentile are not reproduced exactly, so the comparison with the stored series leaves out the first and last 30 s.
# (5) Why the Calb1 records differ: dF/F recomputed from the raw 470 nm signal in the dandiset with the code's
# processing, and with other background fractions, against the published triggered traces.
rows = []
for s in sessions:
binned = lrrk2.raw_binned(s)
key = "6C" if s["line"] == "Anxa1" else "S8D"
pairs = [(d, j) for d, ss, j, idx in rank_match([d for d in src[key] if d["mouse"] == s["mouse"]])]
stored_diff = np.abs(lrrk2.dff_from_raw(binned) - s["dff"])[3000:-3000].max()
best = None
for frac in np.round(np.arange(0.0, 0.951, 0.05), 2):
dff = lrrk2.dff_from_raw(binned, frac)
err = np.median([np.abs(dff[s["onset"][j] - 100:s["onset"][j] + 150] - d["trace"]).max() for d, j in pairs])
if frac == 0.85:
at_085 = err
if best is None or err < best[1]:
best = (frac, err)
rows.append({"mouse": s["mouse"], "line": s["line"], "group": s["group"], "stored_vs_recomputed_max_diff": stored_diff,
"median_diff_at_0.85": at_085, "best_fraction": best[0], "median_diff_at_best": best[1]})
raw_check = pd.DataFrame(rows).sort_values(["line", "group", "mouse"])
print("Largest difference between the stored dF/F and dF/F recomputed from raw with the code's 0.85 (away from the edges),",
"and the median over a mouse's published traces of the largest difference to dF/F recomputed with background fraction",
"0.85 and with the best fraction of 0 to 0.95 (% dF/F):")
print(raw_check.round(3).to_string(index=False))
Largest difference between the stored dF/F and dF/F recomputed from raw with the code's 0.85 (away from the edges), and the median over a mouse's published traces of the largest difference to dF/F recomputed with background fraction 0.85 and with the best fraction of 0 to 0.95 (% dF/F): mouse line group stored_vs_recomputed_max_diff median_diff_at_0.85 best_fraction median_diff_at_best 3742 Anxa1 GS 0.192 0.118 0.85 0.118 3743 Anxa1 GS 0.242 0.145 0.85 0.145 4004 Anxa1 GS 0.086 0.047 0.85 0.047 4005 Anxa1 GS 0.036 0.002 0.85 0.002 4007 Anxa1 GS 0.058 0.000 0.85 0.000 4651 Anxa1 GS 0.242 0.191 0.85 0.191 4652 Anxa1 GS 0.413 0.264 0.85 0.264 4885 Anxa1 GS 0.075 0.017 0.85 0.017 3947 Anxa1 WT 0.860 0.598 0.85 0.598 3948 Anxa1 WT 1.136 0.588 0.85 0.588 4253 Anxa1 WT 0.990 0.835 0.85 0.835 4398 Anxa1 WT 0.871 0.492 0.85 0.492 4401 Anxa1 WT 0.711 0.641 0.85 0.641 4558 Anxa1 WT 0.308 0.076 0.85 0.076 4561 Anxa1 WT 0.152 0.109 0.85 0.109 302 Calb1 GS 0.681 164.686 0.65 39.824 306 Calb1 GS 0.253 113.061 0.80 88.316 740 Calb1 GS 0.195 66.341 0.85 66.341 741 Calb1 GS 1.472 261.222 0.50 40.434 848 Calb1 GS 0.178 78.939 0.75 47.581 852 Calb1 GS 0.278 196.729 0.65 35.871 935 Calb1 GS 0.345 149.468 0.70 41.564 671 Calb1 WT 1.433 371.125 0.55 33.814 673 Calb1 WT 0.127 66.219 0.80 43.890 688 Calb1 WT 0.199 248.842 0.70 58.357 966 Calb1 WT 0.617 103.399 0.80 56.929 1803 Calb1 WT 0.848 518.819 0.25 22.329
# (5b) Is the Calb1 mismatch one of epoch or power labels, or of processing? Per mouse, the fit of the published S8D
# traces on the dandiset's matched traces (published = slope x ours + offset), and for each published trace the
# dandiset epoch, over all 64 epochs and all 8 powers of that mouse, that it is closest to.
s8d = part2[part2.table == "S8D"]
print("S8D traces against the matched dandiset epoch, per mouse (r, slope and offset of published on ours; offset in % dF/F):")
print(s8d.groupby("mouse").agg(traces=("r", "size"), r_median=("r", "median"), r_min=("r", "min"), slope_median=("slope", "median"),
slope_min=("slope", "min"), slope_max=("slope", "max"), offset_median=("offset", "median"),
max_diff_median=("max_diff", "median")).round(3).to_string())
rows = []
for d, s, j, idx in rank_match(src["S8D"]):
chunks = np.array([lrrk2.code_chunk(s, k) for k in range(len(s["onset"]))])
ok = ~np.isnan(chunks).any(1)
diff = np.where(ok, np.nanmax(np.abs(chunks - d["trace"]), 1), np.inf)
r = np.array([np.corrcoef(c, d["trace"])[0, 1] if o else -np.inf for c, o in zip(chunks, ok)])
kd, kr = int(np.argmin(diff)), int(np.argmax(r))
rows.append({"mouse": d["mouse"], "power": d["power"], "matched_epoch": j, "diff_matched": diff[j], "r_matched": r[j],
"best_epoch_by_diff": kd, "best_diff": diff[kd], "best_diff_power": s["power_in_mW"][kd],
"best_epoch_by_r": kr, "best_r": r[kr], "best_r_power": s["power_in_mW"][kr]})
best = pd.DataFrame(rows)
print("\nFor the 384 S8D traces: best epoch by largest absolute difference and by correlation, over all epochs of the mouse")
print("best epoch by difference is the matched one:", int((best.best_epoch_by_diff == best.matched_epoch).sum()), "of", len(best),
"; has the labelled power:", int((best.best_diff_power == best.power).sum()))
print("best epoch by correlation is the matched one:", int((best.best_epoch_by_r == best.matched_epoch).sum()), "of", len(best),
"; has the labelled power:", int((best.best_r_power == best.power).sum()))
print("traces with any epoch within 1e-6:", int((best.best_diff < 1e-6).sum()), "; best difference over all epochs (% dF/F), median",
round(best.best_diff.median(), 2), "range", round(best.best_diff.min(), 2), "to", round(best.best_diff.max(), 2))
print(best.groupby("mouse").agg(traces=("power", "size"), best_by_r_is_matched=("best_epoch_by_r", lambda v: int((v == best.loc[v.index, "matched_epoch"]).sum())),
diff_matched_median=("diff_matched", "median"), best_diff_median=("best_diff", "median"),
r_matched_median=("r_matched", "median"), best_r_median=("best_r", "median")).round(3).to_string())
S8D traces against the matched dandiset epoch, per mouse (r, slope and offset of published on ours; offset in % dF/F):
traces r_median r_min slope_median slope_min slope_max offset_median max_diff_median
mouse
302 32 0.932 0.009 0.398 0.003 0.761 13.185 164.947
306 32 0.766 -0.083 0.722 -0.053 1.309 -13.801 113.055
671 32 0.973 0.616 0.305 0.099 0.423 -0.467 370.041
673 32 0.883 -0.098 0.619 -0.118 1.144 6.924 66.238
688 32 0.971 0.786 0.495 0.301 0.845 9.013 248.914
740 32 0.761 -0.635 0.650 -0.882 1.553 16.240 66.369
741 32 0.922 -0.285 0.293 -0.087 0.405 1.236 262.118
848 32 0.954 0.546 0.721 0.318 1.119 -7.683 78.972
852 32 0.969 0.613 0.435 0.192 0.764 0.775 196.583
935 32 0.943 -0.303 0.448 -0.317 0.666 9.616 149.578
966 32 0.972 0.620 0.747 0.369 1.379 -1.600 103.163
1803 32 0.988 0.712 0.210 0.139 0.271 -1.486 518.390
For the 384 S8D traces: best epoch by largest absolute difference and by correlation, over all epochs of the mouse
best epoch by difference is the matched one: 11 of 384 ; has the labelled power: 128
best epoch by correlation is the matched one: 7 of 384 ; has the labelled power: 86
traces with any epoch within 1e-6: 0 ; best difference over all epochs (% dF/F), median 47.14 range 15.72 to 150.77
traces best_by_r_is_matched diff_matched_median best_diff_median r_matched_median best_r_median
mouse
302 32 0 164.947 38.307 0.932 0.984
306 32 0 113.055 67.579 0.766 0.930
671 32 0 370.041 81.035 0.973 0.994
673 32 1 66.238 34.457 0.883 0.974
688 32 3 248.914 52.934 0.971 0.996
740 32 0 66.369 35.000 0.761 0.938
741 32 0 262.118 63.204 0.922 0.986
848 32 1 78.972 35.062 0.954 0.993
852 32 0 196.583 45.954 0.969 0.994
935 32 1 149.578 33.998 0.943 0.983
966 32 0 103.163 43.580 0.972 0.991
1803 32 1 518.390 98.724 0.988 0.997
Result: not reproduced. All 27 mice in the source data are in the dandiset with the same genotype and line. For Anxa1 the link holds: 120 of 120 triggered traces of Figure 6C and 479 of 480 of Supplementary Figure 7B equal the dandiset's dF/F from 1 s before onset (largest difference among matched traces 5.7e-13 in each table), the published evoked integrals equal the code's measure times 0.0001 for all Anxa1 records except one (119 of 120 at 4 mW), and the transient counts match for 15 of 15 mice. The two Anxa1 misses are the last 4 mW stimulation of mouse 4398, whose processed series ends 0.8 s after onset. For Calb1 none of the 384 traces and none of the 384 integrals match, and the transient counts match for 1 of 12 mice. dF/F recomputed from the raw signal with the code's processing reproduces the stored Calb1 series (largest difference 1.47 % dF/F) but not the published traces (median differences 66 to 519 % dF/F at the code's background fraction, and 22 to 88 at the best fraction for each mouse). The published spontaneous values are 0.005 times the mean dF/F over each transient (ratio 0.004994 to 0.004999 in the 15 Anxa1 mice), not its integral, so part (4) as the criterion fixed it matches for no mouse. Scored as the criterion sets out, the four parts match for 1.0, 0.609, 0.609 and 0 of their records; with the transient mean in part (4), 0.481. The Figure 6E integrals match the 0.5 mW epochs (120 of 120) and none of the 4 mW ones, so the figure uses the power its legend states. The published values reproduce the paper's statistics (Figure 6E p = 1.1e-20, Supplementary Figure 8C p = 0.989, Cohen's d 2.06 and 0.765).
Part (4) scores 0 with integrals at either tolerance, 1% or the 1e-4 of part (3). With the transient mean, 13 of 15 Anxa1 mice match within 1%; mice 4398 and 4007 each fail by one of their 162 and 153 sorted values (1.03% and 1.67%). None matches within 1e-4, as the per-mouse factor varies from 0.004994 to 0.004999.
The Calb1 mismatch is not one of epoch or power labels. Against the matched epochs the published S8D traces have per-mouse median correlations of 0.76 to 0.99, median slopes of 0.21 to 0.75 and median offsets of -13.8 to 16.2 % dF/F. Searched over all 64 epochs and all 8 powers of each mouse, no epoch is within 1e-6 of any of the 384 traces; the closest differs by 15.7 to 150.8 % dF/F (median 47.1). Correlation does not single out an epoch: the epoch with the highest correlation is the matched one for 7 of 384 traces and has the labelled power for 86. The published Calb1 traces are smaller and differently shaped versions of the dandiset's responses, which points to processing; the dandiset does not hold that processing.
Evoked integrals¶
Integrals for every train under the four windows: the Methods' light-on period (main), the code's -0.1 to 1.0 s
(code), the window chunking_index.m computes as written, starting 1 s after onset (code_as_written), and a fixed
0 to 0.5 s (fixed). All use the mean from 1.0 to 0.1 s before onset as baseline (shifted with the window for
code_as_written).
def evoked_table(window):
rows = []
for s in sessions:
values = lrrk2.evoked_integrals(s, window)
for j, v in enumerate(values):
rows.append({"mouse": s["mouse"], "line": s["line"], "group": s["group"], "power": s["power_in_mW"][j],
"train_s": round(s["stop_time"][j] - s["start_time"][j], 2), "value": v})
return pd.DataFrame(rows)
windows = ["methods", "code", "code_as_written", "fixed"]
evoked = {w: evoked_table(w) for w in windows}
print("Evoked integrals (% dF/F x s) that could not be computed (window outside the recording):",
{w: int(evoked[w].value.isna().sum()) for w in windows})
def group_test(df):
gs, wt = df[df.group == "GS"].value.dropna(), df[df.group == "WT"].value.dropna()
t, p = lrrk2.one_tailed_less(gs, wt)
return {"n_GS": len(gs), "n_WT": len(wt), "mean_GS": gs.mean(), "mean_WT": wt.mean(),
"t": t, "p_one_tailed": p, "cohens_d": lrrk2.cohens_d(wt, gs)}
def per_mouse_tests(df, alpha):
wt = df[df.group == "WT"].value.dropna()
rows = []
for m, g in df[df.group == "GS"].groupby("mouse"):
v = g.value.dropna()
row = {"mouse": m, "n": len(v), "mean": v.mean(), "WT_mean": wt.mean()}
for name, equal_var, tails in (("a_welch_1t", False, 1), ("b_welch_2t", False, 2),
("c_student_2t", True, 2), ("d_student_1t", True, 1)):
p = (lrrk2.one_tailed_less(v, wt, equal_var)[1] if tails == 1 else lrrk2.two_tailed(v, wt, equal_var)[1])
row[f"p_{name}"] = p
row[f"sig_{name}"] = bool(p < alpha and v.mean() < wt.mean())
rows.append(row)
return pd.DataFrame(rows)
Evoked integrals (% dF/F x s) that could not be computed (window outside the recording): {'methods': 0, 'code': 1, 'code_as_written': 1, 'fixed': 0}
# Check of evoked_integrals on a synthetic series that ends 0.81 s after the last onset, as mouse 4398's does: the
# light-on window (0.51 s) and the 0 to 0.5 s window fit and are computed; the code's window (to 1.0 s) does not.
toy = {"dff": np.r_[np.zeros(200), np.ones(81)], "onset": np.array([100, 200]), "offset": np.array([150, 251])}
toy_vals = {w: lrrk2.evoked_integrals(toy, w) for w in ("methods", "fixed", "code")}
print("Synthetic check, integrals of the second train per window:", {w: v[1] for w, v in toy_vals.items()})
assert np.isclose(toy_vals["methods"][1], 0.51) and np.isclose(toy_vals["fixed"][1], 0.5) and np.isnan(toy_vals["code"][1])
Synthetic check, integrals of the second train per window: {'methods': np.float64(0.51), 'fixed': np.float64(0.5), 'code': np.float64(nan)}
fig6e-anxa1-evoked-reduced¶
Optogenetically evoked dopamine release in the dorsolateral striatum (Anxa1+ projection region) at 0.5 mW is lower in LRRK2-G2019S than in LRRK2-WT mice (64 against 56 stimulations, p = 1.10e-20, one-tailed two-sample t-test).
The plot shows the mean dF/F (shading: SEM over stimulations) at 0.5 mW, with the 405 nm control dashed, and the light-on period shaded. All points are within the axes; the ranges are printed.
anxa = evoked["methods"].query("line == 'Anxa1' and power == 0.5")
main = group_test(anxa)
print("Anxa1, 0.5 mW, Methods window (light-on period):")
print({k: (float(f"{v:.3g}") if isinstance(v, float) else v) for k, v in main.items()})
print(f"one-tailed p = {main['p_one_tailed']:.3g} (paper 1.10e-20)")
print("\nPer mouse (mean and range of the eight integrals, train length):")
print(anxa.groupby(["group", "mouse"]).agg(n=("value", "size"), mean=("value", "mean"), min=("value", "min"),
max=("value", "max"), train_s=("train_s", "first")).round(2).to_string())
print("\nVariants (window, power): counts, means, one-tailed p, Cohen's d")
rows = []
for w in windows:
for q in (0.5, 4.0):
r = group_test(evoked[w].query("line == 'Anxa1' and power == @q"))
rows.append({"window": w, "power": q, **r})
print(pd.DataFrame(rows).to_string(index=False, float_format=lambda v: f"{v:.3g}"))
print("\nVariant without the two G2019S mice whose 0.5 mW trains are 0.33 s long (3742, 4885), Methods window:")
r = group_test(anxa[~anxa.mouse.isin([3742, 4885])])
print({k: (f"{v:.3g}" if isinstance(v, float) else v) for k, v in r.items()})
print("Variant with one value per mouse (mouse means), Methods window: Welch one-tailed p =",
f"{lrrk2.one_tailed_less(anxa[anxa.group == 'GS'].groupby('mouse').value.mean(), anxa[anxa.group == 'WT'].groupby('mouse').value.mean(), False)[1]:.3g}")
fig, axes = plt.subplots(1, 2, figsize=(11, 3.8))
tt = np.arange(-100, 150) / lrrk2.FS
for ax, line in zip(axes, ("Anxa1", "Calb1")):
for group, colour in (("GS", "tab:blue"), ("WT", "tab:gray")):
tr, iso = [], []
for s in sessions:
if s["line"] != line or s["group"] != group:
continue
for j in np.where(s["power_in_mW"] == 0.5)[0]:
tr.append(lrrk2.code_chunk(s, j))
on = s["onset"][j]
iso.append(s["dff405"][on - 100:on + 150])
tr, iso = np.array(tr), np.array(iso)
m, se = tr.mean(0), tr.std(0, ddof=1) / np.sqrt(len(tr))
ax.plot(tt, m, color=colour, label=f"{group} (n = {len(tr)})")
ax.fill_between(tt, m - se, m + se, color=colour, alpha=0.3)
ax.plot(tt, iso.mean(0), color=colour, ls="--", lw=1)
print(f"{line} {group}: mean trace from {m.min():.1f} to {m.max():.1f} % dF/F; 405 nm mean from {iso.mean(0).min():.1f} to {iso.mean(0).max():.1f}")
ax.axvspan(0, 0.5, color="red", alpha=0.1)
ax.set_title(f"{line}, 0.5 mW (dashed: 405 nm)")
ax.set_xlabel("time from onset (s)")
ax.set_ylabel("dF/F (%)")
ax.legend(frameon=False)
plt.tight_layout()
plt.show()
Anxa1, 0.5 mW, Methods window (light-on period):
{'n_GS': 64, 'n_WT': 56, 'mean_GS': 48.8, 'mean_WT': 128.0, 't': -12.1, 'p_one_tailed': 1.16e-22, 'cohens_d': 2.21}
one-tailed p = 1.16e-22 (paper 1.10e-20)
Per mouse (mean and range of the eight integrals, train length):
n mean min max train_s
group mouse
GS 3742 8 3.28 -5.29 12.15 0.33
3743 8 125.30 97.59 146.77 0.52
4004 8 26.26 24.49 30.16 0.51
4005 8 13.87 8.95 17.21 0.51
4007 8 33.57 26.26 39.15 0.51
4651 8 82.33 76.10 88.09 0.51
4652 8 64.95 53.90 76.01 0.51
4885 8 40.45 33.94 48.55 0.32
WT 3947 8 129.57 114.74 137.02 0.51
3948 8 113.44 97.88 130.57 0.51
4253 8 130.38 88.43 161.16 0.51
4398 8 114.82 96.19 127.53 0.51
4401 8 190.21 157.64 231.96 0.51
4558 8 126.48 109.02 141.47 0.52
4561 8 90.86 80.45 108.23 0.51
Variants (window, power): counts, means, one-tailed p, Cohen's d
window power n_GS n_WT mean_GS mean_WT t p_one_tailed cohens_d
methods 0.5 64 56 48.8 128 -12.1 1.16e-22 2.21
methods 4 64 56 66.3 172 -10.2 4.08e-18 1.86
code 0.5 64 56 75.3 199 -11.2 1.1e-20 2.06
code 4 64 55 99 258 -9.27 5.62e-16 1.7
code_as_written 0.5 64 56 -11.8 -33 4.71 1 -0.861
code_as_written 4 64 55 -22.1 -60.9 6.92 1 -1.27
fixed 0.5 64 56 49.7 124 -11.8 4.73e-22 2.16
fixed 4 64 56 66.8 168 -10.1 4.76e-18 1.85
Variant without the two G2019S mice whose 0.5 mW trains are 0.33 s long (3742, 4885), Methods window:
{'n_GS': 48, 'n_WT': 56, 'mean_GS': '57.7', 'mean_WT': '128', 't': '-10', 'p_one_tailed': '4.16e-17', 'cohens_d': '1.97'}
Variant with one value per mouse (mouse means), Methods window: Welch one-tailed p = 0.000434
Anxa1 GS: mean trace from 15.8 to 144.3 % dF/F; 405 nm mean from 1.3 to 2.5
Anxa1 WT: mean trace from 35.8 to 349.6 % dF/F; 405 nm mean from 1.0 to 3.0
Calb1 GS: mean trace from 40.1 to 220.6 % dF/F; 405 nm mean from 2.8 to 4.3
Calb1 WT: mean trace from 46.4 to 387.1 % dF/F; 405 nm mean from 1.8 to 4.2
Result: reproduced. At 0.5 mW there are 64 GS and 56 WT stimulations, as in the paper. The mean integral over the
light-on period is 48.8 against 128.0 % dF/F x s, one-tailed p = 1.16e-22 (paper 1.10e-20), Cohen's d 2.21. With the
code's window p = 1.1e-20, the paper's value. The reduction holds at 4 mW (p = 4.08e-18), without the two GS mice given
0.33 s trains (p = 4.16e-17), and with one value per mouse (Welch p = 0.000434). The window chunking_index.m computes
as written reverses the sign (one-tailed p = 1, means -11.8 and -33), as it starts 1 s after onset. Every GS mouse
except 3743 has a lower mean than every WT mouse (GS 3.28 to 82.33 without 3743, WT 90.86 to 190.21).
fig6e-anxa1-evoked-per-mouse¶
Seven of eight individual G2019S mice show significantly reduced evoked dopamine release at 0.5 mW compared with the WT group; mouse 3743 does not.
pm = per_mouse_tests(anxa, 0.05 / 8)
print("Each Anxa1 G2019S mouse against all WT integrals, 0.5 mW, Methods window; significant when p < 0.05/8 and the mean is lower.")
print(pm.to_string(index=False, float_format=lambda v: f"{v:.3g}"))
print("\nSignificant mice per reading:", {c[4:]: int(pm[c].sum()) for c in pm.columns if c.startswith("sig_")})
print("Not significant under each reading:", {c[4:]: pm.loc[~pm[c], "mouse"].tolist() for c in pm.columns if c.startswith("sig_")})
print("Paper's p-values (legend): 4885 2.57e-10, 4651 0.0004, 4652 7.36e-05, 3742 4.14e-15, 3743 0.478, 4004 2.81e-13, 4005 1.72e-14, 4007 2.36e-12")
print("\nVariants (other windows, and 4 mW): significant mice per reading")
for w in windows:
for q in (0.5, 4.0):
v = per_mouse_tests(evoked[w].query("line == 'Anxa1' and power == @q"), 0.05 / 8)
print(f" {w:16s} {q} mW:", {c[4:]: int(v[c].sum()) for c in v.columns if c.startswith("sig_")},
"not significant (a):", v.loc[~v.sig_a_welch_1t, "mouse"].tolist())
Each Anxa1 G2019S mouse against all WT integrals, 0.5 mW, Methods window; significant when p < 0.05/8 and the mean is lower.
mouse n mean WT_mean p_a_welch_1t sig_a_welch_1t p_b_welch_2t sig_b_welch_2t p_c_student_2t sig_c_student_2t p_d_student_1t sig_d_student_1t
3742 8 3.28 128 3.69e-34 True 7.38e-34 True 7.46e-16 True 3.73e-16 True
3743 8 125 128 0.362 False 0.725 False 0.821 False 0.41 False
4004 8 26.3 128 3.29e-31 True 6.59e-31 True 1.56e-12 True 7.82e-13 True
4005 8 13.9 128 2.42e-34 True 4.83e-34 True 2.36e-14 True 1.18e-14 True
4007 8 33.6 128 7.35e-30 True 1.47e-29 True 1.98e-11 True 9.9e-12 True
4651 8 82.3 128 7.9e-15 True 1.58e-14 True 0.000202 True 0.000101 True
4652 8 65 128 2.92e-18 True 5.84e-18 True 9.38e-07 True 4.69e-07 True
4885 8 40.5 128 8.84e-28 True 1.77e-27 True 2.17e-10 True 1.08e-10 True
Significant mice per reading: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 7, 'd_student_1t': 7}
Not significant under each reading: {'a_welch_1t': [3743], 'b_welch_2t': [3743], 'c_student_2t': [3743], 'd_student_1t': [3743]}
Paper's p-values (legend): 4885 2.57e-10, 4651 0.0004, 4652 7.36e-05, 3742 4.14e-15, 3743 0.478, 4004 2.81e-13, 4005 1.72e-14, 4007 2.36e-12
Variants (other windows, and 4 mW): significant mice per reading
methods 0.5 mW: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 7, 'd_student_1t': 7} not significant (a): [3743]
methods 4.0 mW: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 6, 'd_student_1t': 7} not significant (a): [3743]
code 0.5 mW: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 7, 'd_student_1t': 7} not significant (a): [3743]
code 4.0 mW: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 6, 'd_student_1t': 6} not significant (a): [3743]
code_as_written 0.5 mW: {'a_welch_1t': 0, 'b_welch_2t': 0, 'c_student_2t': 0, 'd_student_1t': 0} not significant (a): [3742, 3743, 4004, 4005, 4007, 4651, 4652, 4885]
code_as_written 4.0 mW: {'a_welch_1t': 0, 'b_welch_2t': 0, 'c_student_2t': 0, 'd_student_1t': 0} not significant (a): [3742, 3743, 4004, 4005, 4007, 4651, 4652, 4885]
fixed 0.5 mW: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 7, 'd_student_1t': 7} not significant (a): [3743]
fixed 4.0 mW: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 7, 'd_student_1t': 7} not significant (a): [3743]
Result: reproduced. Under all four readings 7 of 8 GS mice are below the WT group at p < 0.05/8, and 3743 is the exception (mean 125 against 128, Welch one-tailed p = 0.362), as in the paper. The Student one-tailed p-values are of the same order as the legend's, for example 1.01e-04 against 4.0e-04 for 4651 and 3.73e-16 against 4.14e-15 for 3742. With the code's window the count is 7 under every reading, and at 4 mW it is 7 under the Welch readings. Two of the seven, 3742 and 4885, received their paper powers as 0.33 s trains, unlike every WT mouse, and the data cannot confirm their power labels (data check); the other five significant mice are 4004, 4005, 4007, 4651 and 4652.
supp7c-anxa1-evoked-all-powers¶
The reduction of evoked dopamine release in Anxa1 G2019S mice holds at each of the four stimulation powers, 0.1, 0.5, 1 and 4 mW (p from 1.29e-15 to 1.10e-20).
rows = []
for w in windows:
for q in lrrk2.PAPER_POWERS:
rows.append({"window": w, "power": q, **group_test(evoked[w].query("line == 'Anxa1' and power == @q"))})
s7 = pd.DataFrame(rows)
print("Anxa1, evoked integrals per power; paper p: 0.1 mW 1.29e-15, 0.5 mW 1.10e-20, 1 mW 1.22e-17, 4 mW 2.39e-16")
print(s7.to_string(index=False, float_format=lambda v: f"{v:.3g}"))
main7 = s7[s7.window == "methods"]
print("\nMethods window: powers with G2019S lower at one-tailed p < 0.05:",
int(((main7.p_one_tailed < 0.05) & (main7.mean_GS < main7.mean_WT)).sum()), "of 4")
Anxa1, evoked integrals per power; paper p: 0.1 mW 1.29e-15, 0.5 mW 1.10e-20, 1 mW 1.22e-17, 4 mW 2.39e-16
window power n_GS n_WT mean_GS mean_WT t p_one_tailed cohens_d
methods 0.1 64 56 30.9 73.6 -8.94 3.13e-15 1.64
methods 0.5 64 56 48.8 128 -12.1 1.16e-22 2.21
methods 1 64 56 56.2 143 -11.4 3.79e-21 2.09
methods 4 64 56 66.3 172 -10.2 4.08e-18 1.86
code 0.1 64 56 51.3 127 -9.11 1.29e-15 1.67
code 0.5 64 56 75.3 199 -11.2 1.1e-20 2.06
code 1 64 56 84.3 211 -9.96 1.22e-17 1.82
code 4 64 55 99 258 -9.27 5.62e-16 1.7
code_as_written 0.1 64 56 -7.56 -23.9 4.46 1 -0.817
code_as_written 0.5 64 56 -11.8 -33 4.71 1 -0.861
code_as_written 1 64 56 -14.8 -38.5 5.4 1 -0.987
code_as_written 4 64 55 -22.1 -60.9 6.92 1 -1.27
fixed 0.1 64 56 32.5 71.6 -8.34 8e-14 1.53
fixed 0.5 64 56 49.7 124 -11.8 4.73e-22 2.16
fixed 1 64 56 57.1 140 -11.4 4.86e-21 2.08
fixed 4 64 56 66.8 168 -10.1 4.76e-18 1.85
Methods window: powers with G2019S lower at one-tailed p < 0.05: 4 of 4
Result: reproduced. GS integrals are lower at all four powers, one-tailed p from 3.13e-15 (0.1 mW) to 1.16e-22 (0.5 mW) with the Methods' window. With the code's window the p-values at 0.1, 0.5 and 1 mW are 1.29e-15, 1.1e-20 and 1.22e-17, the paper's values; at 4 mW it is 5.62e-16 against 2.39e-16, as the code's window, which runs to 1.0 s after onset, leaves 55 WT stimulations in place of 56. The Methods window fits the last 4 mW train of mouse 4398 and keeps all 56.
Spontaneous transients¶
Transients for every mouse, with the Methods' exclusion of 1 s before to 1.5 s after each onset (main) and with the window the code removes as written (1 s to 3.5 s after onset). Each transient's integral, peak and mean dF/F are kept.
def spont_table(exclusion):
rows = []
for s in sessions:
auc, peaks, means = lrrk2.spontaneous_transients(s, exclusion)
rows += [{"mouse": s["mouse"], "line": s["line"], "group": s["group"], "value": a, "peak": p, "mean": m}
for a, p, m in zip(auc, peaks, means)]
return pd.DataFrame(rows)
spont = {ex: spont_table(ex) for ex in ("methods", "code_as_written")}
for ex, df in spont.items():
print(f"{ex}: {len(df)} transients over {df.mouse.nunique()} mice; integral (% dF/F x s) from {df.value.min():.2f} to {df.value.max():.1f}")
methods: 3393 transients over 27 mice; integral (% dF/F x s) from 2.29 to 1522.9 code_as_written: 1826 transients over 27 mice; integral (% dF/F x s) from 11.61 to 14489.2
fig6f-anxa1-spontaneous-reduced¶
Spontaneous dopamine transients in the dorsolateral striatum are smaller in G2019S than in WT mice (1064 against 798 transients, p = 2.75e-177, one-tailed two-sample t-test), with 57 to 285 transients per mouse.
sa = spont["methods"].query("line == 'Anxa1'")
main_s = group_test(sa)
print("Anxa1 spontaneous transients, Methods exclusion:")
print({k: (float(f"{v:.3g}") if isinstance(v, float) else v) for k, v in main_s.items()})
print(f"one-tailed p = {main_s['p_one_tailed']:.3g} (paper 2.75e-177; counts 1064 G2019S, 798 WT)")
per = sa.groupby(["group", "mouse"]).agg(n=("value", "size"), mean=("value", "mean"), median=("value", "median"),
max=("value", "max"), mean_peak=("peak", "mean"))
print("\nPer mouse:")
print(per.round(2).to_string())
print("Transients per mouse from", per.n.min(), "to", per.n.max(), "(paper 57 to 285; criterion 51.3 to 313.5)")
v = spont["code_as_written"].query("line == 'Anxa1'")
r = group_test(v)
print("\nVariant, the code's exclusion window as written:", {k: (f"{x:.3g}" if isinstance(x, float) else x) for k, x in r.items()})
print("Variant, one value per mouse (mouse means), Welch one-tailed p =",
f"{lrrk2.one_tailed_less(per.loc['GS', 'mean'], per.loc['WT', 'mean'], False)[1]:.3g}")
print("Variant, transient peaks in place of integrals: one-tailed p =",
f"{lrrk2.one_tailed_less(sa[sa.group == 'GS'].peak, sa[sa.group == 'WT'].peak)[1]:.3g}")
r = group_test(sa.assign(value=sa["mean"]))
print("Variant, the mean dF/F over each transient (the measure the source data hold, see source-data-link):",
{k: (f"{x:.3g}" if isinstance(x, float) else x) for k, x in r.items()})
Anxa1 spontaneous transients, Methods exclusion:
{'n_GS': 1064, 'n_WT': 798, 'mean_GS': 132.0, 'mean_WT': 303.0, 't': -18.7, 'p_one_tailed': 4.85e-72, 'cohens_d': 0.876}
one-tailed p = 4.85e-72 (paper 2.75e-177; counts 1064 G2019S, 798 WT)
Per mouse:
n mean median max mean_peak
group mouse
GS 3742 285 73.66 48.30 446.40 48.46
3743 132 299.56 272.10 872.05 96.24
4004 143 62.75 51.42 187.28 22.42
4005 122 36.19 26.02 117.86 11.80
4007 153 54.63 42.99 161.53 22.39
4651 57 416.91 390.00 797.53 77.95
4652 79 259.57 242.19 650.17 70.15
4885 93 148.76 152.39 405.11 37.51
WT 3947 81 514.26 504.29 1237.65 117.23
3948 151 282.04 239.81 1067.54 95.23
4253 104 346.03 322.96 919.61 97.38
4398 162 186.41 154.43 596.89 84.03
4401 76 622.04 629.03 1415.50 139.68
4558 93 289.88 234.83 759.53 77.32
4561 131 129.87 88.09 416.05 44.07
Transients per mouse from 57 to 285 (paper 57 to 285; criterion 51.3 to 313.5)
Variant, the code's exclusion window as written: {'n_GS': 648, 'n_WT': 363, 'mean_GS': '300', 'mean_WT': '969', 't': '-18.9', 'p_one_tailed': '1.3e-68', 'cohens_d': '1.24'}
Variant, one value per mouse (mouse means), Welch one-tailed p = 0.0318
Variant, transient peaks in place of integrals: one-tailed p = 1.04e-158
Variant, the mean dF/F over each transient (the measure the source data hold, see source-data-link): {'n_GS': 1064, 'n_WT': 798, 'mean_GS': '24.2', 'mean_WT': '49', 't': '-31.7', 'p_one_tailed': '2.23e-177', 'cohens_d': '1.49'}
Result: reproduced. The Methods' detection gives 1064 GS and 798 WT transients, the paper's counts, with 57 to 285
per mouse, the paper's range. Mean integrals are 131.9 against 302.9 % dF/F x s, one-tailed p = 4.85e-72 (paper
2.75e-177). The published values are the mean dF/F over each transient (see source-data-link); with that measure
p = 2.23e-177. With one value per mouse the difference gives Welch p = 0.0318, and the code's exclusion window as
written gives p = 1.3e-68 on 648 and 363 transients.
fig6f-anxa1-spontaneous-per-mouse¶
Six of eight individual G2019S mice show significantly smaller spontaneous dopamine transients than the WT group; mice 4651 and 3743 do not.
pms = per_mouse_tests(sa, 0.05 / 8)
print("Each Anxa1 G2019S mouse's transient integrals against all WT transients, Methods exclusion; threshold 0.05/8.")
print(pms.to_string(index=False, float_format=lambda v: f"{v:.3g}"))
print("\nSignificant mice per reading:", {c[4:]: int(pms[c].sum()) for c in pms.columns if c.startswith("sig_")})
print("Not significant under each reading:", {c[4:]: pms.loc[~pms[c], "mouse"].tolist() for c in pms.columns if c.startswith("sig_")})
print("Paper's p-values (legend): 4885 1.44e-49, 4651 0.008, 4652 6.21e-13, 3742 5.28e-88, 3743 0.97, 4004 1.24e-95, 4005 4.13e-109, 4007 5.00e-107")
v = per_mouse_tests(sa.assign(value=sa["mean"]), 0.05 / 8)
print("\nVariant, the mean dF/F over each transient:", {c[4:]: int(v[c].sum()) for c in v.columns if c.startswith("sig_")},
"not significant (a):", v.loc[~v.sig_a_welch_1t, "mouse"].tolist())
print(v[["mouse", "mean", "WT_mean", "p_a_welch_1t", "p_d_student_1t"]].to_string(index=False, float_format=lambda x: f"{x:.3g}"))
v = per_mouse_tests(spont["code_as_written"].query("line == 'Anxa1'"), 0.05 / 8)
print("\nVariant, code's exclusion as written:", {c[4:]: int(v[c].sum()) for c in v.columns if c.startswith("sig_")},
"not significant (a):", v.loc[~v.sig_a_welch_1t, "mouse"].tolist())
Each Anxa1 G2019S mouse's transient integrals against all WT transients, Methods exclusion; threshold 0.05/8.
mouse n mean WT_mean p_a_welch_1t sig_a_welch_1t p_b_welch_2t sig_b_welch_2t p_c_student_2t sig_c_student_2t p_d_student_1t sig_d_student_1t
3742 285 73.7 303 7.2e-102 True 1.44e-101 True 5.9e-50 True 2.95e-50 True
3743 132 300 303 0.431 False 0.862 False 0.883 False 0.441 False
4004 143 62.7 303 8.67e-109 True 1.73e-108 True 7.83e-30 True 3.92e-30 True
4005 122 36.2 303 3.36e-136 True 6.73e-136 True 2.75e-31 True 1.38e-31 True
4007 153 54.6 303 1.46e-118 True 2.91e-118 True 1.25e-33 True 6.27e-34 True
4651 57 417 303 1 False 1.44e-08 False 0.000482 False 1 False
4652 79 260 303 0.0128 False 0.0256 False 0.122 False 0.0608 False
4885 93 149 303 1.28e-30 True 2.55e-30 True 2.01e-09 True 1e-09 True
Significant mice per reading: {'a_welch_1t': 5, 'b_welch_2t': 5, 'c_student_2t': 5, 'd_student_1t': 5}
Not significant under each reading: {'a_welch_1t': [3743, 4651, 4652], 'b_welch_2t': [3743, 4651, 4652], 'c_student_2t': [3743, 4651, 4652], 'd_student_1t': [3743, 4651, 4652]}
Paper's p-values (legend): 4885 1.44e-49, 4651 0.008, 4652 6.21e-13, 3742 5.28e-88, 3743 0.97, 4004 1.24e-95, 4005 4.13e-109, 4007 5.00e-107
Variant, the mean dF/F over each transient: {'a_welch_1t': 7, 'b_welch_2t': 7, 'c_student_2t': 6, 'd_student_1t': 6} not significant (a): [3743]
mouse mean WT_mean p_a_welch_1t p_d_student_1t
3742 25.3 49 5.23e-165 5.38e-88
3743 52 49 0.989 0.965
4004 13.7 49 8.09e-279 1.12e-95
4005 7.26 49 0 3.33e-109
4007 12.3 49 1.89e-290 4.43e-107
4651 43.2 49 1.5e-05 0.00819
4652 34 49 5.71e-16 5.73e-13
4885 19.4 49 2.6e-128 1.28e-49
Variant, code's exclusion as written: {'a_welch_1t': 6, 'b_welch_2t': 6, 'c_student_2t': 6, 'd_student_1t': 6} not significant (a): [3743, 4885]
Result: partially reproduced. By the integral, 5 of 8 GS mice are below the WT group under every reading. The exceptions are 3743 (Welch one-tailed p = 0.431), 4651, whose mean (417) is above the WT mean (303), and 4652 (p = 0.0128). With the mean dF/F over each transient, the measure of the published values, reading (d) gives 6 of 8 with 3743 and 4651 as the exceptions, and p-values close to the legend's (4651 0.00819 against 0.008, 3743 0.965 against 0.97, 3742 5.38e-88 against 5.28e-88). The Welch readings give 7 there, as 4651 reaches p = 1.5e-05, so the paper's count follows the one-tailed Student test of its legend, not the Welch test of its Methods.
supp8c-calb1-spontaneous-not-reduced¶
In the dorsomedial striatum (Calb1+ projection region), spontaneous dopamine transients are not reduced in G2019S mice (970 against 682 transients, p = 0.98).
sc = spont["methods"].query("line == 'Calb1'")
r = group_test(sc)
print("Calb1 spontaneous transients, Methods exclusion (paper: 970 G2019S, 682 WT, one-tailed p = 0.98):")
print({k: (float(f"{v:.4g}") if isinstance(v, float) else v) for k, v in r.items()})
print(f"one-tailed p (G2019S lower) = {r['p_one_tailed']:.3g}; difference of means (WT - G2019S) = {r['mean_WT'] - r['mean_GS']:.3f} % dF/F x s")
print("Two-tailed p =", f"{lrrk2.two_tailed(sc[sc.group == 'GS'].value, sc[sc.group == 'WT'].value)[1]:.3g}")
per = sc.groupby(["group", "mouse"]).agg(n=("value", "size"), mean=("value", "mean"), median=("value", "median"))
print("\nPer mouse:")
print(per.round(3).to_string())
v = spont["code_as_written"].query("line == 'Calb1'")
r2 = group_test(v)
print("\nVariant, the code's exclusion window as written:", {k: (f"{x:.3g}" if isinstance(x, float) else x) for k, x in r2.items()})
print("Variant, mouse means, Welch one-tailed p =",
f"{lrrk2.one_tailed_less(per.loc['GS', 'mean'], per.loc['WT', 'mean'], False)[1]:.3g}")
r3 = group_test(sc.assign(value=sc["mean"]))
print("Variant, the mean dF/F over each transient:", {k: (f"{x:.3g}" if isinstance(x, float) else x) for k, x in r3.items()})
Calb1 spontaneous transients, Methods exclusion (paper: 970 G2019S, 682 WT, one-tailed p = 0.98):
{'n_GS': 896, 'n_WT': 635, 'mean_GS': 322.9, 'mean_WT': 343.9, 't': -1.637, 'p_one_tailed': 0.05088, 'cohens_d': 0.08494}
one-tailed p (G2019S lower) = 0.0509; difference of means (WT - G2019S) = 21.086 % dF/F x s
Two-tailed p = 0.102
Per mouse:
n mean median
group mouse
GS 302 93 490.660 408.085
306 75 510.731 489.758
740 140 252.220 225.825
741 158 416.694 347.068
848 120 343.685 317.679
852 165 188.790 159.445
935 145 219.291 174.492
WT 671 159 311.234 249.236
673 159 206.032 150.697
688 106 375.352 327.453
966 79 488.571 454.854
1803 132 437.652 367.420
Variant, the code's exclusion window as written: {'n_GS': 540, 'n_WT': 275, 'mean_GS': '696', 'mean_WT': '1.08e+03', 't': '-4.15', 'p_one_tailed': '1.81e-05', 'cohens_d': '0.308'}
Variant, mouse means, Welch one-tailed p = 0.402
Variant, the mean dF/F over each transient: {'n_GS': 896, 'n_WT': 635, 'mean_GS': '61.2', 'mean_WT': '67.5', 't': '-5.9', 'p_one_tailed': '2.25e-09', 'cohens_d': '0.306'}
Result: reproduced. By the paper's test, GS transients are not significantly smaller: one-tailed p = 0.0509 on 896
GS and 635 WT transients (both within tolerance of 970 and 682), difference of means 21.1 % dF/F x s, Cohen's d 0.085.
The result sits at the threshold. With the mean dF/F over each transient, the measure of the published values, GS are
lower at p = 2.25e-09 (d = 0.306), and with the code's exclusion window as written at p = 1.81e-05; with one value per
mouse, Welch p = 0.402. The dandiset's Calb1 dF/F is not the trace the paper analysed (see source-data-link), and
the published values give p = 0.989.
supp8e-calb1-evoked-reduced¶
Evoked dopamine release in the dorsomedial striatum from Calb1+ neuron stimulation is reduced in G2019S mice at each of four powers (56 against 40 stimulations per power, p from 6.45e-05 to 0.001).
rows = []
for w in windows:
for q in lrrk2.PAPER_POWERS:
rows.append({"window": w, "power": q, **group_test(evoked[w].query("line == 'Calb1' and power == @q"))})
s8 = pd.DataFrame(rows)
print("Calb1, evoked integrals per power; paper p: 0.1 mW 6.45e-05, 0.5 mW 0.0002, 1 mW 0.0001, 4 mW 0.001 (56 against 40)")
print(s8.to_string(index=False, float_format=lambda v: f"{v:.3g}"))
main8 = s8[s8.window == "methods"]
print("\nMethods window: powers with G2019S lower at one-tailed p < 0.05:",
int(((main8.p_one_tailed < 0.05) & (main8.mean_GS < main8.mean_WT)).sum()), "of 4")
cal = evoked["methods"].query("line == 'Calb1' and power == 0.5")
print("\nPer mouse, 0.5 mW, Methods window:")
print(cal.groupby(["group", "mouse"]).value.agg(["size", "mean", "min", "max"]).round(2).to_string())
print("\nVariant with one value per mouse (mouse means, 7 GS and 5 WT), Methods window, Welch one-tailed p per power:")
for q in lrrk2.PAPER_POWERS:
mm = evoked["methods"].query("line == 'Calb1' and power == @q").groupby(["group", "mouse"]).value.mean()
print(f" {q} mW: p = {lrrk2.one_tailed_less(mm.loc['GS'], mm.loc['WT'], False)[1]:.3g}, mean of mouse means GS {mm.loc['GS'].mean():.1f}, WT {mm.loc['WT'].mean():.1f}")
Calb1, evoked integrals per power; paper p: 0.1 mW 6.45e-05, 0.5 mW 0.0002, 1 mW 0.0001, 4 mW 0.001 (56 against 40)
window power n_GS n_WT mean_GS mean_WT t p_one_tailed cohens_d
methods 0.1 56 40 23.8 55.9 -5.7 6.9e-08 1.18
methods 0.5 56 40 70.4 131 -6.74 6.29e-10 1.4
methods 1 56 40 101 171 -5.8 4.51e-08 1.2
methods 4 56 40 145 247 -5.27 4.38e-07 1.09
code 0.1 56 40 40.7 101 -5.69 7.07e-08 1.18
code 0.5 56 40 116 212 -6.05 1.47e-08 1.25
code 1 56 40 159 269 -5.33 3.36e-07 1.1
code 4 56 40 217 379 -5.06 1.04e-06 1.05
code_as_written 0.1 56 40 -7.61 -20.6 1.67 0.95 -0.345
code_as_written 0.5 56 40 -14.5 -32.4 2.53 0.993 -0.524
code_as_written 1 56 40 -19.2 -43.2 3.08 0.999 -0.638
code_as_written 4 56 40 -23.5 -66.6 4.33 1 -0.897
fixed 0.1 56 40 22.9 53.9 -5.7 6.82e-08 1.18
fixed 0.5 56 40 68.1 127 -6.75 6.14e-10 1.4
fixed 1 56 40 97.7 166 -5.81 4.26e-08 1.2
fixed 4 56 40 141 241 -5.28 4.1e-07 1.09
Methods window: powers with G2019S lower at one-tailed p < 0.05: 4 of 4
Per mouse, 0.5 mW, Methods window:
size mean min max
group mouse
GS 302 8 70.54 59.12 80.03
306 8 64.52 45.09 93.07
740 8 17.35 -12.01 32.04
741 8 94.20 79.07 113.97
848 8 58.25 41.07 72.77
852 8 100.29 73.90 123.85
935 8 87.93 64.88 98.22
WT 671 8 155.87 103.09 206.26
673 8 49.74 37.39 59.02
688 8 169.98 118.81 199.98
966 8 90.10 71.13 104.04
1803 8 190.17 142.78 240.39
Variant with one value per mouse (mouse means, 7 GS and 5 WT), Methods window, Welch one-tailed p per power:
0.1 mW: p = 0.0424, mean of mouse means GS 23.8, WT 55.9
0.5 mW: p = 0.0413, mean of mouse means GS 70.4, WT 131.2
1.0 mW: p = 0.0726, mean of mouse means GS 100.6, WT 171.2
4.0 mW: p = 0.0916, mean of mouse means GS 144.9, WT 247.4
Result: reproduced. There are 56 GS and 40 WT stimulations per power, as in the paper. GS are lower at all four
powers, one-tailed p from 4.38e-07 (4 mW) to 6.29e-10 (0.5 mW), smaller p-values than the paper's 6.45e-05 to 0.001;
the published integrals do not come from the dandiset's Calb1 dF/F (see source-data-link). With one value per mouse
(7 GS and 5 WT), Welch one-tailed p is 0.0424, 0.0413, 0.0726 and 0.0916 at 0.1, 0.5, 1 and 4 mW, so the reduction is
significant across mice at the two lower powers only.
supp8e-calb1-few-mice-reduced¶
Only 2 of 7 G2019S mice show reduced Calb1+ evoked dopamine release relative to the WT group.
pmc = per_mouse_tests(cal, 0.05 / 7)
print("Each Calb1 G2019S mouse against all WT integrals, 0.5 mW, Methods window; threshold 0.05/7.")
print(pmc.to_string(index=False, float_format=lambda v: f"{v:.3g}"))
print("\nSignificant mice per reading:", {c[4:]: int(pmc[c].sum()) for c in pmc.columns if c.startswith("sig_")})
print("Significant under each reading:", {c[4:]: pmc.loc[pmc[c], "mouse"].tolist() for c in pmc.columns if c.startswith("sig_")})
print("\nVariants (other powers and windows): significant mice per reading")
for w in windows:
for q in lrrk2.PAPER_POWERS:
v = per_mouse_tests(evoked[w].query("line == 'Calb1' and power == @q"), 0.05 / 7)
print(f" {w:16s} {q} mW:", {c[4:]: int(v[c].sum()) for c in v.columns if c.startswith("sig_")})
Each Calb1 G2019S mouse against all WT integrals, 0.5 mW, Methods window; threshold 0.05/7.
mouse n mean WT_mean p_a_welch_1t sig_a_welch_1t p_b_welch_2t sig_b_welch_2t p_c_student_2t sig_c_student_2t p_d_student_1t sig_d_student_1t
302 8 70.5 131 5.41e-08 True 1.08e-07 True 0.00524 True 0.00262 True
306 8 64.5 131 3.94e-07 True 7.88e-07 True 0.00249 True 0.00125 True
740 8 17.3 131 1.62e-14 True 3.23e-14 True 1.7e-06 True 8.48e-07 True
741 8 94.2 131 0.000274 True 0.000549 True 0.0809 False 0.0404 False
848 8 58.2 131 2.35e-09 True 4.71e-09 True 0.00099 True 0.000495 True
852 8 100 131 0.00436 True 0.00871 False 0.145 False 0.0725 False
935 8 87.9 131 3.92e-05 True 7.84e-05 True 0.0424 False 0.0212 False
Significant mice per reading: {'a_welch_1t': 7, 'b_welch_2t': 6, 'c_student_2t': 4, 'd_student_1t': 4}
Significant under each reading: {'a_welch_1t': [302, 306, 740, 741, 848, 852, 935], 'b_welch_2t': [302, 306, 740, 741, 848, 935], 'c_student_2t': [302, 306, 740, 848], 'd_student_1t': [302, 306, 740, 848]}
Variants (other powers and windows): significant mice per reading
methods 0.1 mW: {'a_welch_1t': 5, 'b_welch_2t': 5, 'c_student_2t': 2, 'd_student_1t': 2}
methods 0.5 mW: {'a_welch_1t': 7, 'b_welch_2t': 6, 'c_student_2t': 4, 'd_student_1t': 4}
methods 1.0 mW: {'a_welch_1t': 7, 'b_welch_2t': 6, 'c_student_2t': 2, 'd_student_1t': 2}
methods 4.0 mW: {'a_welch_1t': 5, 'b_welch_2t': 5, 'c_student_2t': 1, 'd_student_1t': 3}
code 0.1 mW: {'a_welch_1t': 5, 'b_welch_2t': 5, 'c_student_2t': 2, 'd_student_1t': 2}
code 0.5 mW: {'a_welch_1t': 6, 'b_welch_2t': 6, 'c_student_2t': 2, 'd_student_1t': 3}
code 1.0 mW: {'a_welch_1t': 7, 'b_welch_2t': 5, 'c_student_2t': 1, 'd_student_1t': 2}
code 4.0 mW: {'a_welch_1t': 6, 'b_welch_2t': 5, 'c_student_2t': 1, 'd_student_1t': 1}
code_as_written 0.1 mW: {'a_welch_1t': 0, 'b_welch_2t': 0, 'c_student_2t': 0, 'd_student_1t': 0}
code_as_written 0.5 mW: {'a_welch_1t': 0, 'b_welch_2t': 0, 'c_student_2t': 0, 'd_student_1t': 0}
code_as_written 1.0 mW: {'a_welch_1t': 0, 'b_welch_2t': 0, 'c_student_2t': 0, 'd_student_1t': 0}
code_as_written 4.0 mW: {'a_welch_1t': 0, 'b_welch_2t': 0, 'c_student_2t': 0, 'd_student_1t': 0}
fixed 0.1 mW: {'a_welch_1t': 5, 'b_welch_2t': 5, 'c_student_2t': 2, 'd_student_1t': 2}
fixed 0.5 mW: {'a_welch_1t': 7, 'b_welch_2t': 6, 'c_student_2t': 4, 'd_student_1t': 4}
fixed 1.0 mW: {'a_welch_1t': 7, 'b_welch_2t': 6, 'c_student_2t': 2, 'd_student_1t': 2}
fixed 4.0 mW: {'a_welch_1t': 5, 'b_welch_2t': 5, 'c_student_2t': 1, 'd_student_1t': 3}
Result: partially reproduced. At 0.5 mW, the number of GS mice below the WT group at p < 0.05/7 is 7 (Welch one-tailed), 6 (Welch two-tailed) and 4 (Student, either tail), against the paper's 2; the closest reading gives 4. Student tests give 2 at 0.1 and 1 mW and 1 or 3 at 4 mW, so the count depends on power and test. Mouse 740 (mean 17.3 against 131) is below the WT group under every reading.
cohens-d-anxa1-larger¶
The G2019S reduction of evoked release at 0.5 mW is much larger for Anxa1+ (Cohen's d = 2.05) than for Calb1+ stimulation (d = 0.76).
rows = []
for w in windows:
for q in lrrk2.PAPER_POWERS:
da = lrrk2.cohens_d(*[evoked[w].query("line == 'Anxa1' and power == @q and group == @g").value.dropna() for g in ("WT", "GS")])
dc = lrrk2.cohens_d(*[evoked[w].query("line == 'Calb1' and power == @q and group == @g").value.dropna() for g in ("WT", "GS")])
rows.append({"window": w, "power": q, "d_Anxa1": da, "d_Calb1": dc, "ratio": da / dc})
cd = pd.DataFrame(rows)
print("Cohen's d (WT minus G2019S over the pooled SD); paper at 0.5 mW: Anxa1 2.05, Calb1 0.76")
print(cd.to_string(index=False, float_format=lambda v: f"{v:.3g}"))
m = cd.query("window == 'methods' and power == 0.5").iloc[0]
print(f"\nMethods window, 0.5 mW: Anxa1 d = {m.d_Anxa1:.3f} (tolerance 1.845 to 2.255), Calb1 d = {m.d_Calb1:.3f} (0.684 to 0.836), ratio {m.ratio:.2f}")
Cohen's d (WT minus G2019S over the pooled SD); paper at 0.5 mW: Anxa1 2.05, Calb1 0.76
window power d_Anxa1 d_Calb1 ratio
methods 0.1 1.64 1.18 1.39
methods 0.5 2.21 1.4 1.58
methods 1 2.09 1.2 1.74
methods 4 1.86 1.09 1.71
code 0.1 1.67 1.18 1.41
code 0.5 2.06 1.25 1.64
code 1 1.82 1.1 1.65
code 4 1.7 1.05 1.63
code_as_written 0.1 -0.817 -0.345 2.37
code_as_written 0.5 -0.861 -0.524 1.64
code_as_written 1 -0.987 -0.638 1.55
code_as_written 4 -1.27 -0.897 1.42
fixed 0.1 1.53 1.18 1.29
fixed 0.5 2.16 1.4 1.55
fixed 1 2.08 1.2 1.73
fixed 4 1.85 1.09 1.7
Methods window, 0.5 mW: Anxa1 d = 2.210 (tolerance 1.845 to 2.255), Calb1 d = 1.395 (0.684 to 0.836), ratio 1.58
Result: not reproduced. At 0.5 mW, Cohen's d is 2.21 for Anxa1, within tolerance of 2.05, and 1.40 for Calb1,
against 0.76 (tolerance 0.684 to 0.836); their ratio is 1.58, below the factor of 2 the criterion asks. With the code's
window d is 2.06 and 1.25. The published integrals give 2.06 and 0.765 (see source-data-link), so the paper's values
follow from its own data, and the Calb1 difference is consistent with the Calb1 dF/F in the dandiset not being the one
the paper analysed. The Anxa1 effect is larger than the Calb1 effect at every power under the Methods, code and fixed
windows (ratio 1.29 to 1.74).
Blocked claims¶
These claims rest on data that are not in the dandiset, which holds only the photometry of Figure 6 and Supplementary
Figures 7 and 8. The data exist elsewhere: images in the BioImage Archive (S-BIAD2070, S-BIAD2071, S-BIAD2072,
S-BIAD2074), mass spectrometry in PRIDE (PXD065003, PXD065006, PXD065125), and per-section and per-blot values in the
source data file on Zenodo. The registry judges the dandiset, so they are recorded as blocked (data_missing).
| Claim | Paper | Data |
|---|---|---|
fig1d-lrrk2-enriched-vulnerable |
LRRK2 higher in Aldh1a1+ SNc neurons | immunostaining images |
fig2b-rab3-phosphorylation |
289 phosphopeptides change with MLi-2 | phosphoproteomics |
fig2eg-prab3-increased |
pRAB3A and pRAB3C up in G2019S | PhosTag western blots |
fig3d-rim-binds-unphosphorylated |
RIM1 and RIM2 prefer unphosphorylated RAB3A | affinity purification MS |
fig4d-fewer-active-zones |
Fewer bassoon+ TH+ synaptosomes | confocal images |
fig4i-rim1-reduced |
RIM1 reduced in synaptosomes | western blots |
fig5-bassoon-vulnerable-axons |
Bassoon clusters change in vulnerable axons | SIM images |
fig5j-no-axon-loss |
No loss of TH+ fibres | confocal images |
Summary¶
| Claim | Outcome |
|---|---|
source-data-link |
not reproduced (Anxa1 links; Calb1 does not) |
fig6e-anxa1-evoked-reduced |
reproduced |
fig6e-anxa1-evoked-per-mouse |
reproduced |
supp7c-anxa1-evoked-all-powers |
reproduced |
fig6f-anxa1-spontaneous-reduced |
reproduced |
fig6f-anxa1-spontaneous-per-mouse |
partially reproduced |
supp8c-calb1-spontaneous-not-reduced |
reproduced (p = 0.0509, at the threshold) |
supp8e-calb1-evoked-reduced |
reproduced |
supp8e-calb1-few-mice-reduced |
partially reproduced |
cohens-d-anxa1-larger |
not reproduced |
| Eight claims of Figures 1 to 5 | blocked, data missing |
A failure to reproduce here may be a failure of this attempt, not of the paper: see the reasoning and the differences from the paper's method.
How the analysis differs from the paper's:
- The evoked integral is taken over the light-on period, as the Methods state; the paper's numbers come from the code's window, 0.1 s before to 1.0 s after onset (its published integrals equal that measure times 0.0001). Both are shown.
- Spontaneous transients are measured by their integral, as the Methods state; the paper's published values are 0.005 times the mean dF/F over each transient. Both are shown.
- Per-mouse tests are shown under four readings, as the Methods (Welch), the legends (one-tailed) and the code (Student, two-tailed) disagree.
- The window
chunking_index.mcomputes as written starts 1 s after onset; the source data show the paper used the intended window starting 1 s before onset. The literal window is shown as a variant only.
What was found about the dandiset:
- The Anxa1 recordings are those behind Figure 6 and Supplementary Figure 7: the matched published traces equal the dandiset's dF/F to within 5.7e-13, and the evoked integrals and transient counts match, except one stimulation of mouse 4398 that the processed series cuts off 0.8 s after onset.
- The Calb1 dF/F in the dandiset is not the trace behind Supplementary Figure 8. None of the 384 published traces or integrals match it, no epoch of any power matches a published trace, and recomputing from the raw signal with any single background fraction does not close the gap. The raw signal is there, but the processing the paper used for Calb1 is not, so the Calb1 results here are of the dandiset's processing, not the paper's.
- The epochs table describes one kind of train and four powers; the files hold two kinds of train and eight powers, and two Anxa1 GS mice received the paper's powers as 0.33 s trains of 9 ms pulses; in these two mice the response does not follow the labelled power.
- The dF/F unit is given as
a.u.for a series in percent; two trains have a pulse length of 0; the four excluded mice are left out without a note; and the paper does not cite the dandiset.
print(f"total run time {time.time() - started:.0f} s")
total run time 195 s