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.

In [1]:
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.

In [2]:
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.

In [3]:
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.

In [4]:
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.

In [5]:
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.

In [6]:
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.

In [7]:
# (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
In [8]:
# (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).

In [9]:
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}
In [10]:
# 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.

In [11]:
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
No description has been provided for this image

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.

In [12]:
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).

In [13]:
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.

In [14]:
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.

In [15]:
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.

In [16]:
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).

In [17]:
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).

In [18]:
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.

In [19]:
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).

In [20]:
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.m computes 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.
In [21]:
print(f"total run time {time.time() - started:.0f} s")
total run time 195 s