Kauvar, Richman, Liu et al. (2025): human eyepuff behaviour and dissociation, from DANDI:001347¶

Paper: Kauvar, Richman, Liu et al., "Conserved brain-wide emergence of emotional response from sensory experience in humans and mice", Science 388, eadt3971 (2025), doi:10.1126/science.adt3971.

Dataset: DANDI:001347, version 0.250528.0702. It is the only published version (28 May 2025, the day before the paper appeared); the paper cites the dandiset without a version, so the latest published version is pinned. Its content (207 assets, 16.0 GB) is the same size and asset count as the current draft.

Claims file: claims/001347/10.1126-science.adt3971.yaml, written from the version of record with its Materials and Methods, figure legends and the Supplementary Materials (Table S3).

This notebook tests the claims on human eye closure and dissociation (CADSS). Other notebooks of the entry test the mouse behaviour (02), the iEEG spectrograms (03), the iEEG coding dimension (04) and the mouse Neuropixels data of DANDI:001326 (05).

Claim Tested here
p50-condition-labels-link (link) tested
cadss-scores-link (link) tested
fig1e-human-late-eye-closure-ketamine tested (with the code's condition labels)
figs1b-human-early-eye-closure-unchanged tested (with the code's condition labels)
figs1c-human-late-eye-closure-decrease tested (with the code's condition labels)
figs1d-human-cadss-saline-ketamine tested (CADSS from the code record)
fig1f-human-subjective-reports blocked: the verbal reports are not in the dataset
figs8a-ieeg-inclusion-per-participant tested
figs8b-ieeg-excluded-participants tested
fig2c-ieeg-cohort-selective-affective-decrease tested
fig2d-ieeg-cadss-reversible tested (CADSS from the code record)
figs14g-cadss-correlates-with-affective-decrease tested (CADSS from the code record)
In [1]:
import warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
import scipy.stats as st
import matplotlib.pyplot as plt
from statsmodels.stats.multitest import multipletests

import dandi_io
import human_eye as he

print("dandiset", dandi_io.DANDISET, "version", dandi_io.VERSION)
assets = dandi_io.list_assets()
print(len(assets), "assets in the pinned version")
dandiset 001347 version 0.250528.0702
207 assets in the pinned version

Data check¶

The non-iEEG files (sub-h*) hold two series of 40 eye-closure trials each, pre_trials_data and post_trials_data, with a time axis time_reference/tt (seconds from the puff). The iEEG behaviour files (sub-SD*_ses-puff_behavior.nwb) hold, per session, the continuous eye closure, the video frame of each puff (events-*-on), the trial windows (by_trial_closure, 800 samples by trials), the stored per-trial early, late and late/early values, the early and late windows, the frame rate, and which_trials.

In [2]:
p50 = {}
rows = []
for sub in he.P50_SUBJECTS:
    tt, pre, post = he.load_p50(sub)
    p50[sub] = (tt, pre, post)
    for name, x in [("pre", pre), ("post", post)]:
        rows.append({"subject": sub, "series": name, "trials": x.shape[0], "samples": x.shape[1],
                     "t first (s)": tt[0], "t last (s)": round(tt[-1], 3),
                     "rate (Hz)": round(1 / np.median(np.diff(tt)), 3),
                     "min": round(np.nanmin(x), 3), "max": round(np.nanmax(x), 3),
                     "NaN": int(np.isnan(x).sum())})
print("Non-iEEG cohort (eye closure, fraction of the open eye):")
print(pd.DataFrame(rows).to_string(index=False))
Non-iEEG cohort (eye closure, fraction of the open eye):
subject series  trials  samples  t first (s)  t last (s)  rate (Hz)    min   max  NaN
   h009    pre      40      800        -1.53       2.465      200.0 -0.055 0.976    0
   h009   post      40      800        -1.53       2.465      200.0 -0.259 2.739    0
   h011    pre      40      800        -1.53       2.465      200.0 -0.170 0.880    0
   h011   post      40      800        -1.53       2.465      200.0 -0.089 0.844    0
   h012    pre      40      800        -1.53       2.465      200.0 -0.139 0.932    0
   h012   post      40      800        -1.53       2.465      200.0 -0.382 0.913    0
   h013    pre      40      800        -1.53       2.465      200.0 -0.159 0.910    0
   h013   post      40      800        -1.53       2.465      200.0 -0.086 0.941    0
In [3]:
beh = {sub: he.load_ieeg_behavior(sub) for sub in he.IEEG_SUBJECTS}
rows = []
for sub, b in beh.items():
    for sess in he.SESSIONS:
        s = b[sess]
        rows.append({"subject": sub, "session": sess, "file session": s["file_session"],
                     "trials": s["trials"].shape[0], "samples": s["trials"].shape[1],
                     "puffs": len(s["events_on"]), "which_trials": len(b["which_trials"]),
                     "continuous (min)": round(len(s["eyeclosure"]) / b["fps"] / 60, 1),
                     "trace min": round(np.nanmin(s["trials"]), 2),
                     "trace max": round(np.nanmax(s["trials"]), 2)})
info = pd.DataFrame(rows)
print("iEEG cohort behaviour files:")
print(info.to_string(index=False))
print("windows stored in every file: early", {tuple(b["ref_w"]) for b in beh.values()},
      "late", {tuple(b["aff_w"]) for b in beh.values()}, "fps", {b["fps"] for b in beh.values()})
iEEG cohort behaviour files:
subject      session file session  trials  samples  puffs  which_trials  continuous (min)  trace min  trace max
  SD163  preinfusion  preinfusion      40      800     40            39               6.2      -0.16       0.92
  SD163     infusion     infusion      40      800     40            39               6.1      -0.06       0.92
  SD163 postinfusion postinfusion      40      800     40            39               6.4      -0.17       0.93
  SD168  preinfusion     baseline      40      800     40            24              13.3      -0.04       0.97
  SD168     infusion     infusion      40      800     40            24               6.9      -0.12       0.99
  SD168 postinfusion postinfusion      40      800     40            24               6.0      -0.11       1.01
  SD170  preinfusion  preinfusion      40      800     40            39               6.5      -0.25       1.00
  SD170     infusion     infusion      40      800     40            39               6.4      -0.39       0.90
  SD170 postinfusion postinfusion      40      800     40            39               6.3      -0.32       0.96
  SD172  preinfusion     baseline      40      800     40            39               6.1      -0.04       1.00
  SD172     infusion     infusion      40      800     40            39               6.1      -0.25       0.96
  SD172 postinfusion postinfusion      40      800     40            39               6.4      -0.09       0.99
  SD178  preinfusion  preinfusion      32      800     32            27               5.6      -0.25       1.00
  SD178     infusion     infusion      32      800     32            27               5.1      -0.12       0.98
  SD178 postinfusion postinfusion      32      800     32            27               5.8      -0.16       1.00
  SD181  preinfusion  preinfusion      40      800     40            30               7.2      -0.17       0.98
  SD181     infusion     infusion      40      800     40            30               7.2      -0.37       0.93
  SD181 postinfusion postinfusion      40      800     40            30               6.6      -0.24       0.99
  SD184  preinfusion  preinfusion      40      800     40            30               6.9      -0.19       0.99
  SD184     infusion     infusion      40      800     40            30               6.0      -0.22       0.95
  SD184 postinfusion postinfusion      40      800     40            30               6.7      -0.02       0.99
  SD185  preinfusion  preinfusion      40      800     40            31               6.2      -0.20       1.00
  SD185     infusion     infusion      32      800     32            31               5.6      -0.27       1.00
  SD185 postinfusion postinfusion      40      800     40            31               7.4      -0.07       0.99
  SD186  preinfusion  preinfusion      40      800     40            12               6.0      -0.12       1.02
  SD186     infusion     infusion      32      800     32            12              11.7      -0.04       2.58
  SD186 postinfusion postinfusion      32      800     32            12               5.3      -0.08       1.02
  SD207  preinfusion  preinfusion      40      800     40            11              11.4      -0.15       0.95
  SD207     infusion     infusion      14      800     14            11               1.4      -0.08       0.96
  SD207 postinfusion postinfusion      40      800     40            11               6.8      -0.49       0.91
  SD211  preinfusion  preinfusion      40      800     40            39               6.4      -0.26       1.00
  SD211     infusion     infusion      40      800     40            39               6.0      -0.12       0.93
  SD211 postinfusion postinfusion      40      800     40            39               6.6      -0.08       0.96
  SD215  preinfusion  preinfusion      40      800     40            39               6.0      -0.37       1.01
  SD215     infusion     infusion      40      800     40            39               6.8      -0.35       0.71
  SD215 postinfusion postinfusion      40      800     40            39               6.0      -0.06       0.94
  SD242  preinfusion     baseline      40      800     40            39               6.1      -0.06       0.97
  SD242     infusion     infusion      40      800     40            39               8.4      -0.06       0.97
  SD242 postinfusion postinfusion      40      800     40            39               6.7      -0.08       0.93
  SD245  preinfusion     baseline      40      800     40            39               6.8      -0.14       1.01
  SD245     infusion     infusion      40      800     40            39               6.1      -0.19       0.92
  SD245 postinfusion postinfusion      40      800     40            39               6.0      -0.18       0.91
windows stored in every file: early {(np.float64(0.1), np.float64(0.2))} late {(np.float64(0.3), np.float64(0.8))} fps {200.0}

Checks of the stored values against the traces they come from. (1) Each trial window of by_trial_closure should be the continuous trace from 400 samples before to 400 after the puff frame. (2) The stored early and late values should be the window means of the trial windows, and the stored late/early their ratio. (3) The puff frames should be spaced as the Methods describe: blocks of 8 puffs 3 to 8 s apart, 35 s between blocks.

In [4]:
rows = []
for sub, b in beh.items():
    for sess in he.SESSIONS:
        s = b[sess]
        ec, ev, tr = s["eyeclosure"], s["events_on"], s["trials"]
        ok = [i for i, e in enumerate(ev) if e - 400 >= 0 and e + 400 <= len(ec)]
        d_win = max(np.nanmax(np.abs(ec[ev[i] - 400:ev[i] + 400] - tr[i])) for i in ok)
        early = he.window_mean(tr, he.EARLY_W)
        late = he.window_mean(tr, he.LATE_W)
        gaps = np.diff(ev) / b["fps"]
        rows.append({"subject": sub, "session": sess, "windows checked": len(ok),
                     "max |window - trace|": d_win,
                     "max |early - stored|": np.nanmax(np.abs(early - s["reflexive"])),
                     "max |late - stored|": np.nanmax(np.abs(late - s["affective"])),
                     "max |ratio - stored|": np.nanmax(np.abs(late / early - s["affective_normed"])),
                     "puff gap min (s)": round(gaps.min(), 2), "median": round(np.median(gaps), 2),
                     "max": round(gaps.max(), 2), "gaps > 20 s": int((gaps > 20).sum())})
chk = pd.DataFrame(rows)
print(chk.to_string(index=False))
subject      session  windows checked  max |window - trace|  max |early - stored|  max |late - stored|  max |ratio - stored|  puff gap min (s)  median   max  gaps > 20 s
  SD163  preinfusion               40                   0.0          2.220446e-16         4.440892e-16          7.771561e-16              3.52    5.54 36.53            4
  SD163     infusion               40                   0.0          2.220446e-16         3.330669e-16          6.661338e-16              3.51    5.53 36.54            4
  SD163 postinfusion               40                   0.0          2.220446e-16         4.440892e-16          8.881784e-16              3.52    5.54 36.56            4
  SD168  preinfusion               40                   0.0          3.330669e-16         7.771561e-16          1.332268e-15              3.50    5.52 36.52            4
  SD168     infusion               40                   0.0          2.220446e-16         8.881784e-16          8.881784e-16              3.50    5.52 36.52            4
  SD168 postinfusion               40                   0.0          2.220446e-16         7.771561e-16          7.771561e-16              3.50    5.52 36.52            4
  SD170  preinfusion               40                   0.0          2.220446e-16         3.330669e-16          3.330669e-16              3.50    5.52 36.52            4
  SD170     infusion               40                   0.0          2.220446e-16         3.330669e-16          4.440892e-16              3.50    5.52 36.52            4
  SD170 postinfusion               40                   0.0          2.220446e-16         1.665335e-16          1.665335e-16              3.50    5.52 36.53            4
  SD172  preinfusion               40                   0.0          2.220446e-16         3.885781e-16          5.551115e-16              3.50    5.52 36.52            4
  SD172     infusion               40                   0.0          2.220446e-16         2.775558e-16          3.885781e-16              3.33    5.52 36.52            4
  SD172 postinfusion               40                   0.0          3.330669e-16         5.551115e-16          6.661338e-16              3.50    5.52 36.52            4
  SD178  preinfusion               32                   0.0          2.220446e-16         6.661338e-16          8.881784e-16              3.50    5.52 36.52            3
  SD178     infusion               32                   0.0          3.330669e-16         9.992007e-16          1.332268e-15              3.50    5.51 36.52            3
  SD178 postinfusion               32                   0.0          3.330669e-16         6.661338e-16          6.661338e-16              3.50    5.52 36.51            3
  SD181  preinfusion               40                   0.0          2.220446e-16         6.661338e-16          1.421085e-14              3.50    5.52 36.53            4
  SD181     infusion               40                   0.0          4.440892e-16         4.440892e-16          6.661338e-16              3.50    5.51 36.52            4
  SD181 postinfusion               40                   0.0          2.220446e-16         4.440892e-16          5.551115e-16              3.50    5.52 36.52            4
  SD184  preinfusion               40                   0.0          2.220446e-16         3.330669e-16          4.440892e-16              3.50    5.52 36.52            4
  SD184     infusion               40                   0.0          3.330669e-16         4.440892e-16          3.885781e-16              3.51    5.52 36.52            4
  SD184 postinfusion               40                   0.0          2.220446e-16         4.440892e-16          5.551115e-16              3.50    5.51 36.52            4
  SD185  preinfusion               40                   0.0          4.440892e-16         5.551115e-16          4.440892e-16              3.51    5.52 36.52            4
  SD185     infusion               32                   0.0          3.330669e-16         3.885781e-16          4.440892e-16              3.50    5.51 36.52            3
  SD185 postinfusion               40                   0.0          2.220446e-16         3.330669e-16          4.440892e-16              3.50    5.52 36.52            4
  SD186  preinfusion               40                   0.0          3.330669e-16         3.330669e-16          5.551115e-16              3.50    5.52 36.52            4
  SD186     infusion               32                   0.0          3.330669e-16         4.440892e-16          7.771561e-16              3.50    5.52 36.52            3
  SD186 postinfusion               32                   0.0          3.330669e-16         4.440892e-16          8.881784e-16              3.50    5.52 36.52            3
  SD207  preinfusion               40                   0.0          3.330669e-16         2.775558e-16          3.330669e-16              3.51    5.52 36.52            4
  SD207     infusion               14                   0.0          2.220446e-16         1.387779e-16          1.665335e-16              1.40    4.54  9.31            0
  SD207 postinfusion               40                   0.0          2.220446e-16         2.220446e-16          4.973799e-14              3.51    5.51 36.52            4
  SD211  preinfusion               40                   0.0          2.220446e-16         2.220446e-16          3.552714e-15              3.50    5.52 36.52            4
  SD211     infusion               40                   0.0          2.220446e-16         1.110223e-16          1.421085e-14              3.51    5.52 36.52            4
  SD211 postinfusion               40                   0.0          3.330669e-16         1.665335e-16          2.220446e-16              3.51    5.52 36.52            4
  SD215  preinfusion               40                   0.0          2.220446e-16         6.661338e-16          8.881784e-16              3.50    5.52 36.52            4
  SD215     infusion               40                   0.0          1.110223e-16         2.220446e-16          5.551115e-16              3.51    5.52 36.52            4
  SD215 postinfusion               40                   0.0          2.220446e-16         6.661338e-16          1.065814e-14              3.50    5.52 36.52            4
  SD242  preinfusion               40                   0.0          2.220446e-16         8.881784e-16          2.842171e-14              3.51    5.52 36.52            4
  SD242     infusion               40                   0.0          2.220446e-16         5.551115e-16          7.771561e-16              3.51    5.52 36.52            4
  SD242 postinfusion               40                   0.0          2.220446e-16         4.440892e-16          4.440892e-16              3.50    5.52 36.52            4
  SD245  preinfusion               40                   0.0          3.330669e-16         6.661338e-16          9.992007e-16              3.50    5.52 36.52            4
  SD245     infusion               40                   0.0          3.330669e-16         3.330669e-16          4.996004e-16              3.50    5.52 36.52            4
  SD245 postinfusion               40                   0.0          2.220446e-16         4.440892e-16          4.440892e-16              3.51    5.52 36.52            4

The trial windows equal the continuous trace around each puff frame, and the stored per-trial values equal the window means and their ratio to rounding, in every participant and session, so the stored values and the traces agree (the table above). The shortest gap between puffs is 3.3 to 3.5 s and the median about 5.5 s, with pauses of about 36.5 s between blocks (4 pauses in 40-puff sessions, 3 in 32-puff sessions), consistent with the Methods (3 to 8 s between puffs, 35 s between blocks). Some sessions hold fewer than 40 puffs (SD178, SD185 and SD186 have 32, SD207's infusion session 14). Some trace values exceed 1, which a fraction of eye closure cannot (h009 ketamine series up to 2.74, SD186 infusion up to 2.58), and are tracking artefacts; they are kept, as in the paper's code. Whether the puff frames are the true puff times cannot be shown from the eye alone; the iEEG coding-dimension notebook (04) shows the puff times share the LFP clock.

which_trials holds 0-based trial indices; in most files it starts at 1, so the first trial is left out, as the Methods say ("the first trial was excluded").

In [5]:
print({sub: (len(b["which_trials"]), int(b["which_trials"].min()), int(b["which_trials"].max()))
       for sub, b in beh.items()})
{'SD163': (39, 1, 39), 'SD168': (24, 1, 39), 'SD170': (39, 1, 39), 'SD172': (39, 1, 39), 'SD178': (27, 2, 31), 'SD181': (30, 1, 31), 'SD184': (30, 1, 31), 'SD185': (31, 1, 31), 'SD186': (12, 1, 13), 'SD207': (11, 1, 13), 'SD211': (39, 1, 39), 'SD215': (39, 1, 39), 'SD242': (39, 1, 39), 'SD245': (39, 1, 39)}

p50-condition-labels-link: which non-iEEG series is saline¶

The non-iEEG files do not say which of the two series is the saline visit. The paper's code (Zenodo 14920570, human_ieeg/scripts/analyze_airpuff_p50.py) reads pre as saline and post as ketamine (treatment saline for pre_trials, highdose for post_trials), and lists the eight sessions by participant with their treatment and CADSS. The link holds when the code's participants are the dataset's, each with both treatments, and the series have the trial counts the paper reports (40, of which 38 are used). The meaning of pre and post itself cannot be checked against anything in the data; the visit order was crossed over (the code's session names put saline first for h009, h012 and h013 and ketamine first for h011), so pre does not mean the first visit.

In [6]:
subjects, cadss_p50 = he.p50_code_labels()
code_tab = pd.DataFrame([{"session": s, "participant": s.split("_")[0], "treatment": t,
                          "visit": int(s.split("_")[1]), "CADSS": cadss_p50[s]} for s, t in subjects])
print(code_tab.to_string(index=False))
print("participants in the code:", sorted(code_tab.participant.unique()))
print("participants in the dataset:", sorted(p50))
print("each with one saline and one ketamine session:",
      all(sorted(g.treatment) == ["highdose", "saline"] for _, g in code_tab.groupby("participant")))
first = code_tab.sort_values("visit").groupby("participant").first().treatment
print("treatment of the first visit:", first.to_dict())
print("trials per series in the dataset:", {s: (v[1].shape[0], v[2].shape[0]) for s, v in p50.items()})
          session participant treatment  visit  CADSS
           h009_1        h009    saline      1      0
           h009_3        h009  highdose      3     13
    h011_1_112836        h011  highdose      1      7
    h011_2_111511        h011    saline      2      0
    h012_1_105053        h012    saline      1      1
h012_2_111359trim        h012  highdose      2      9
    h013_1_110713        h013    saline      1      1
    h013_3_115544        h013  highdose      3     10
participants in the code: ['h009', 'h011', 'h012', 'h013']
participants in the dataset: ['h009', 'h011', 'h012', 'h013']
each with one saline and one ketamine session: True
treatment of the first visit: {'h009': 'saline', 'h011': 'highdose', 'h012': 'saline', 'h013': 'saline'}
trials per series in the dataset: {'h009': (40, 40), 'h011': (40, 40), 'h012': (40, 40), 'h013': (40, 40)}

Result: the link holds for identity and counts. The four participants of the code's list (h009, h011, h012, h013) are the four in the dataset, each with one saline and one ketamine session, and every series has 40 trials. Which series is saline rests on the code alone: the files do not label it, and the first visit was saline for three participants and ketamine for one, so the series names do not encode the visit order either.

cadss-scores-link: CADSS scores of the code record¶

The CADSS scores are not in the dataset. The code record holds the iEEG cohort's item scores per participant and time point (data/cadss_across_pts.csv). The link holds when every participant with behaviour in the dataset is in the table, with a total before ketamine, during infusion and at 110 min.

In [7]:
cad = he.load_cadss()
print(cad.drop(columns="columns").to_string())
missing = [s for s in he.IEEG_SUBJECTS if s not in cad.index]
print("dataset participants missing from the table:", missing)
print("table participants without behaviour in the dataset:", [p for p in cad.index if p not in he.IEEG_SUBJECTS])
have = cad.loc[[s for s in he.IEEG_SUBJECTS if s in cad.index], ["pre", "infusion", "post"]]
print("dataset participants with all three totals:", int(have.notna().all(axis=1).sum()), "of", len(he.IEEG_SUBJECTS))
print("SD185 columns:", cad.loc["SD185", "columns"])
       pre  infusion  post  retro
pt                               
SD140    0      13.0   0.0    NaN
SD152    3      22.0   7.0    NaN
SD157    2      22.0   3.0    NaN
SD160    0      21.0   0.0    NaN
SD163    0       1.0   0.0    NaN
SD168    0       9.0   3.0    NaN
SD169    0      32.0   6.0    NaN
SD170    2       6.0   3.0    NaN
SD172    0      30.0   0.0    NaN
SD178    0       2.0   0.0   15.0
SD181    0      10.0   0.0    NaN
SD184    0      38.0   5.0   54.0
SD185    0       0.0   0.0   10.0
SD186    1      33.0   2.0   20.0
SD191    0       6.0   NaN   14.0
SD195    0       4.0   0.0   14.0
SD207    0       4.0   0.0    4.0
SD211    3      26.0   0.0   31.0
SD215    1      23.0   0.0   27.0
SD242    0       1.0   0.0    1.0
SD245    2      39.0   1.0   54.0
dataset participants missing from the table: []
table participants without behaviour in the dataset: ['SD140', 'SD152', 'SD157', 'SD160', 'SD169', 'SD191', 'SD195']
dataset participants with all three totals: 14 of 14
SD185 columns: Before Ket, 12 mins, 33 mins, (retro) 50 mins, 110 mins, Next Day

Result: the link holds. All 14 iEEG participants with behaviour in the dataset are in the code record's CADSS table, each with a total before ketamine, during infusion and at 110 min (the printed count); the table also holds 7 participants without behaviour in the dataset. The match is by the participant identifier, which both use. Nothing in the dataset can be compared with the scores.

fig1e-human-late-eye-closure-ketamine¶

In the non-iEEG human cohort, late eye closure normalised by early eye closure is lower during ketamine than during saline (0.71 against 0.43, 4 participants).

Method H-EYE (claims file): per participant and series, trials 3 to 40, late (0.3 to 0.8 s) and early (0.1 to 0.2 s) means; the late/early value is the mean late over the mean early (Open B, reading a). Paired t-test across the 4 participants. The readings of Open A (first two trials or the first one excluded) and Open B (ratio of means or mean of ratios) are all shown. Saline is the pre series, by the code's label (link above). The eye-closure traces in the files are already normalised by the open-eye size; the normalisation (median eye size over the first 2 s of the first trial window, claims file H-EYE) cannot be repeated, as the eye sizes are not in these files.

In [8]:
readings = {"A(a) trials 3-40": np.arange(2, 40), "A(b) trials 2-40": np.arange(1, 40)}
p50_rows = []
for sub, (tt, pre, post) in p50.items():
    for rname, idx in readings.items():
        for ratio in ["ratio_of_means", "mean_of_ratios"]:
            for cond, x in [("saline", pre), ("ketamine", post)]:
                e, l, r = he.p50_values(tt, x, idx, ratio)
                p50_rows.append({"subject": sub, "trials": rname, "ratio": ratio, "condition": cond,
                                 "early": e, "late": l, "late/early": r, "n trials": len(idx)})
p50_df = pd.DataFrame(p50_rows)

def paired(df, value, trials, ratio="ratio_of_means"):
    d = df[(df.trials == trials) & (df.ratio == ratio)].pivot(index="subject", columns="condition", values=value)
    t = st.ttest_rel(d.saline, d.ketamine)
    return d, t

out = []
for trials in readings:
    for ratio in ["ratio_of_means", "mean_of_ratios"]:
        d, t = paired(p50_df, "late/early", trials, ratio)
        out.append({"trials": trials, "ratio": ratio, "saline mean": d.saline.mean(), "saline sem": st.sem(d.saline),
                    "ketamine mean": d.ketamine.mean(), "ketamine sem": st.sem(d.ketamine),
                    "t": t.statistic, "p": t.pvalue, "participants lower on ketamine": int((d.ketamine < d.saline).sum())})
print(pd.DataFrame(out).round(4).to_string(index=False))
d, t = paired(p50_df, "late/early", "A(a) trials 3-40")
print("\nPer participant, main reading (A(a), ratio of means):")
print(d.round(3).to_string())
print("expected: saline 0.71 +/- 0.08, ketamine 0.43 +/- 0.12; paper t = 3.703, p = 0.0342")
          trials          ratio  saline mean  saline sem  ketamine mean  ketamine sem      t      p  participants lower on ketamine
A(a) trials 3-40 ratio_of_means       0.7064      0.0843         0.4255        0.1213 3.7034 0.0342                               4
A(a) trials 3-40 mean_of_ratios       0.7049      0.0843         0.4263        0.1211 3.6810 0.0347                               4
A(b) trials 2-40 ratio_of_means       0.7043      0.0850         0.4240        0.1170 3.9121 0.0297                               4
A(b) trials 2-40 mean_of_ratios       0.7029      0.0851         0.4244        0.1173 3.8777 0.0304                               4

Per participant, main reading (A(a), ratio of means):
condition  ketamine  saline
subject                    
h009          0.519   0.908
h011          0.065   0.497
h012          0.527   0.697
h013          0.591   0.724
expected: saline 0.71 +/- 0.08, ketamine 0.43 +/- 0.12; paper t = 3.703, p = 0.0342
In [9]:
fig, ax = plt.subplots(1, 4, figsize=(12, 2.6), sharey=True)
for a, (sub, (tt, pre, post)) in zip(ax, p50.items()):
    a.plot(tt, pre[2:40].mean(0), label="saline (pre)")
    a.plot(tt, post[2:40].mean(0), label="ketamine (post)")
    a.axvspan(*he.EARLY_W, alpha=0.1); a.axvspan(*he.LATE_W, alpha=0.1, color="r")
    a.set_title(sub); a.set_xlabel("time from puff (s)")
ax[0].set_ylabel("eye closure"); ax[0].legend(fontsize=7)
plt.tight_layout(); plt.show()
print("plotted range of the mean traces:", round(min(min(v[1][2:40].mean(0).min(), v[2][2:40].mean(0).min()) for v in p50.values()), 3),
      "to", round(max(max(v[1][2:40].mean(0).max(), v[2][2:40].mean(0).max()) for v in p50.values()), 3))
No description has been provided for this image
plotted range of the mean traces: -0.008 to 0.906

Result: reproduced. With trials 3 to 40 and the ratio of trial means, late/early is 0.706 on saline and 0.426 on ketamine (both within tolerance), lower on ketamine in all 4 participants, paired t = 3.703, p = 0.0342, the values of Table S3. The other three readings also give ketamine lower with p < 0.05 (table above). The result rests on the code's label of the pre series as saline.

figs1b-human-early-eye-closure-unchanged¶

In the non-iEEG cohort, early (reflexive) eye closure does not differ between saline and ketamine (0.84 against 0.78).

A claim of absence, judged on the paper's test: paired t-test across the 4 participants of the mean early eye closure, trials 3 to 40.

In [10]:
d, t = paired(p50_df, "early", "A(a) trials 3-40")
print(d.round(3).to_string())
print(f"saline {d.saline.mean():.3f} (sem {st.sem(d.saline):.3f}), ketamine {d.ketamine.mean():.3f} (sem {st.sem(d.ketamine):.3f})")
print(f"paired t = {t.statistic:.3f}, p = {t.pvalue:.4f}; difference {d.saline.mean() - d.ketamine.mean():.3f}, "
      f"Cohen dz = {(d.saline - d.ketamine).mean() / (d.saline - d.ketamine).std(ddof=1):.2f}")
d2, t2 = paired(p50_df, "early", "A(b) trials 2-40")
print(f"variant, trials 2-40: saline {d2.saline.mean():.3f}, ketamine {d2.ketamine.mean():.3f}, t = {t2.statistic:.3f}, p = {t2.pvalue:.4f}")
print("expected: saline 0.84 +/- 0.01, ketamine 0.78 +/- 0.04; paper t = 1.622, p = 0.2032")
condition  ketamine  saline
subject                    
h009          0.735   0.808
h011          0.685   0.834
h012          0.812   0.876
h013          0.876   0.838
saline 0.839 (sem 0.014), ketamine 0.777 (sem 0.042)
paired t = 1.622, p = 0.2032; difference 0.062, Cohen dz = 0.81
variant, trials 2-40: saline 0.839, ketamine 0.778, t = 1.617, p = 0.2042
expected: saline 0.84 +/- 0.01, ketamine 0.78 +/- 0.04; paper t = 1.622, p = 0.2032

Result: reproduced. Early eye closure is 0.839 on saline and 0.777 on ketamine, paired t = 1.622, p = 0.2032, not significant, as in Table S3; with trials 2 to 40 the test is also not significant.

figs1c-human-late-eye-closure-decrease¶

In the non-iEEG cohort, late (affective) eye closure alone is lower during ketamine than during saline (0.59 against 0.34).

In [11]:
for trials in readings:
    d, t = paired(p50_df, "late", trials)
    print(f"{trials}: saline {d.saline.mean():.3f} (sem {st.sem(d.saline):.3f}), ketamine {d.ketamine.mean():.3f} "
          f"(sem {st.sem(d.ketamine):.3f}), t = {t.statistic:.3f}, p = {t.pvalue:.4f}, lower in {(d.ketamine < d.saline).sum()} of 4")
print("expected: saline 0.59 +/- 0.07, ketamine 0.34 +/- 0.10; paper t = 3.653, p = 0.0354")
A(a) trials 3-40: saline 0.591 (sem 0.066), ketamine 0.343 (sem 0.103), t = 3.653, p = 0.0354, lower in 4 of 4
A(b) trials 2-40: saline 0.590 (sem 0.067), ketamine 0.341 (sem 0.100), t = 3.813, p = 0.0317, lower in 4 of 4
expected: saline 0.59 +/- 0.07, ketamine 0.34 +/- 0.10; paper t = 3.653, p = 0.0354

Result: reproduced. Late eye closure is 0.591 on saline and 0.343 on ketamine with trials 3 to 40, paired t = 3.653, p = 0.0354, lower in all 4 participants; trials 2 to 40 give the same conclusion.

figs1d-human-cadss-saline-ketamine¶

In the non-iEEG cohort, dissociation (CADSS) is higher with ketamine than with saline (9.75 against 0.50).

The scores are not in the dataset; the criterion allows the code's constants (analyze_airpuff_p50.py, the cadss dictionary), read here from the code record.

In [12]:
cd = code_tab.pivot(index="participant", columns="treatment", values="CADSS")
t = st.ttest_rel(cd.saline, cd.highdose)
print(cd.to_string())
print(f"saline {cd.saline.mean():.2f} (sem {st.sem(cd.saline):.2f}), ketamine {cd.highdose.mean():.2f} (sem {st.sem(cd.highdose):.2f})")
print(f"paired t = {t.statistic:.3f}, p = {t.pvalue:.4f}")
print("expected: saline 0.50 +/- 0.29, ketamine 9.75 +/- 1.25; paper t = 7.034, p = 0.006")
treatment    highdose  saline
participant                  
h009               13       0
h011                7       0
h012                9       1
h013               10       1
saline 0.50 (sem 0.29), ketamine 9.75 (sem 1.25)
paired t = -7.034, p = 0.0059
expected: saline 0.50 +/- 0.29, ketamine 9.75 +/- 1.25; paper t = 7.034, p = 0.006

Result: reproduced, on the code record's scores. CADSS is 0.50 on saline and 9.75 on ketamine, paired t = -7.034 (saline minus ketamine), p = 0.0059. These are the code's own numbers; nothing in the dataset bears on them.

fig1f-human-subjective-reports¶

Blocked. The participants' verbal reports are not in the dataset: the four sub-h* files hold only the eye-closure trials and their time axis (listed in the data check), and no other asset holds text from the non-iEEG cohort.

figs8a-ieeg-inclusion-per-participant¶

Seven iEEG participants met the behavioural inclusion criteria, each showing a significant decrease of late/early eye closure from preinfusion to ketamine and a significant recovery from ketamine to postinfusion.

Per participant, the per-trial late/early of the trials in which_trials, recomputed from the trial windows (they equal the stored values, data check). Trials are paired across sessions by position. Paired t-tests preinfusion against ketamine and ketamine against postinfusion, Benjamini-Hochberg over the two (claims file; code airpuff_utils, Annotator with t-test_paired and fdr_bh). NaN trials (where the eye was not tracked) are dropped pairwise.

In [13]:
def inclusion_tests(sub):
    b = beh[sub]
    v = {s: he.per_trial(b, s)["late_over_early"].to_numpy() for s in he.SESSIONS}
    res = {"subject": sub, "n which_trials": len(b["which_trials"])}
    ps = []
    for a, c, lab in [("preinfusion", "infusion", "pre"), ("postinfusion", "infusion", "post")]:
        ok = ~np.isnan(v[a]) & ~np.isnan(v[c])
        t = st.ttest_rel(v[a][ok], v[c][ok])
        res[f"t {lab}-ket"] = t.statistic
        res[f"n {lab}-ket"] = int(ok.sum())
        ps.append(t.pvalue)
    pc = multipletests(ps, method="fdr_bh")[1]
    res["p pre-ket (BH)"], res["p post-ket (BH)"] = pc
    for s in he.SESSIONS:
        res[f"mean {s[:4]}"] = np.nanmean(v[s])
        res[f"sem {s[:4]}"] = st.sem(v[s], nan_policy="omit")
    res["decrease"] = (res["t pre-ket"] > 0) and (pc[0] < 0.05)
    res["recovery"] = (res["t post-ket"] > 0) and (pc[1] < 0.05)
    res["meets both"] = res["decrease"] and res["recovery"]
    return res

inc = pd.DataFrame([inclusion_tests(s) for s in he.IEEG_SUBJECTS]).set_index("subject")
pd.set_option("display.width", 250)
print(inc.loc[he.INCLUDED].round(4).to_string())
         n which_trials  t pre-ket  n pre-ket  t post-ket  n post-ket  p pre-ket (BH)  p post-ket (BH)  mean prei  sem prei  mean infu  sem infu  mean post  sem post  decrease  recovery  meets both
subject                                                                                                                                                                                              
SD168                24     5.8087         24      3.2763          24          0.0000           0.0033     0.9397    0.0133     0.8321    0.0186     0.8968    0.0102      True      True        True
SD172                39     5.6281         39     11.7475          39          0.0000           0.0000     0.5323    0.0298     0.3498    0.0177     0.7526    0.0280      True      True        True
SD181                30    10.5245         30      5.8804          30          0.0000           0.0000     0.8481    0.0422     0.2112    0.0413     0.5332    0.0381      True      True        True
SD185                31     4.7462         31      6.8624          31          0.0000           0.0000     0.6035    0.0311     0.4520    0.0275     0.6684    0.0205      True      True        True
SD186                12     2.7912         12      2.6265          12          0.0236           0.0236     0.5417    0.0474     0.3532    0.0360     0.5538    0.0573      True      True        True
SD211                39     7.1858         39     10.4531          39          0.0000           0.0000     0.3119    0.0193     0.1534    0.0151     0.3587    0.0135      True      True        True
SD245                39     5.2881         39      2.9258          39          0.0000           0.0058     0.5474    0.0155     0.4022    0.0246     0.5046    0.0222      True      True        True
In [14]:
import yaml, pathlib
claims = yaml.safe_load(pathlib.Path("../../claims/001347/10.1126-science.adt3971.yaml").read_text())["claims"]
exp8a = {e["quantity"]: (e["value"], e["tolerance"]) for e in next(c for c in claims if c["id"] == "figs8a-ieeg-inclusion-per-participant")["expected"]}
rows = []
for q, (val, tol) in exp8a.items():
    sub, _, sess = q.split(" ")
    col = {"preinfusion": "mean prei", "ketamine": "mean infu", "postinfusion": "mean post"}[sess]
    m = inc.loc[sub, col]
    rows.append({"quantity": q, "expected": val, "tolerance": tol, "measured": round(m, 3), "within": abs(m - val) <= tol + 1e-9})
cmp8a = pd.DataFrame(rows)
print(cmp8a.to_string(index=False))
print("means within tolerance:", int(cmp8a.within.sum()), "of", len(cmp8a))
print("participants meeting both tests:", int(inc.loc[he.INCLUDED, "meets both"].sum()), "of 7")
print("trial counts (pairs) for the 7:", inc.loc[he.INCLUDED, "n pre-ket"].to_dict())
                     quantity  expected  tolerance  measured  within
 SD168 late/early preinfusion      0.94       0.01     0.940    True
    SD168 late/early ketamine      0.83       0.02     0.832    True
SD168 late/early postinfusion      0.90       0.01     0.897    True
 SD172 late/early preinfusion      0.53       0.03     0.532    True
    SD172 late/early ketamine      0.35       0.02     0.350    True
SD172 late/early postinfusion      0.75       0.03     0.753    True
 SD181 late/early preinfusion      0.85       0.04     0.848    True
    SD181 late/early ketamine      0.21       0.04     0.211    True
SD181 late/early postinfusion      0.53       0.04     0.533    True
 SD185 late/early preinfusion      0.60       0.03     0.604    True
    SD185 late/early ketamine      0.45       0.03     0.452    True
SD185 late/early postinfusion      0.67       0.02     0.668    True
 SD186 late/early preinfusion      0.54       0.05     0.542    True
    SD186 late/early ketamine      0.35       0.04     0.353    True
SD186 late/early postinfusion      0.55       0.06     0.554    True
 SD211 late/early preinfusion      0.31       0.02     0.312    True
    SD211 late/early ketamine      0.15       0.02     0.153    True
SD211 late/early postinfusion      0.36       0.01     0.359    True
 SD245 late/early preinfusion      0.56       0.02     0.547    True
    SD245 late/early ketamine      0.40       0.02     0.402    True
SD245 late/early postinfusion      0.50       0.02     0.505    True
means within tolerance: 21 of 21
participants meeting both tests: 7 of 7
trial counts (pairs) for the 7: {'SD168': 24, 'SD172': 39, 'SD181': 30, 'SD185': 31, 'SD186': 12, 'SD211': 39, 'SD245': 39}

Result: reproduced. All 7 participants show a significant decrease from preinfusion to ketamine and a significant recovery after it (14 of 14 corrected tests p < 0.05 in the stated direction), with the trial counts of Table S3 (24, 39, 30, 31, 12, 39 and 39), and 21 of 21 session means within tolerance (tables above). Thirteen of the 14 t values equal Table S3's to the printed digits; SD245's preinfusion against ketamine test gives t = 5.288 where the table has 5.2553.

figs8b-ieeg-excluded-participants¶

The other seven iEEG eyepuff participants did not meet the inclusion criteria, each lacking a significant ketamine decrease or a significant recovery.

In [15]:
excl = [s for s in he.IEEG_SUBJECTS if s not in he.INCLUDED]
print(inc.loc[excl, ["n which_trials", "t pre-ket", "p pre-ket (BH)", "t post-ket", "p post-ket (BH)",
                     "mean prei", "mean infu", "mean post", "decrease", "recovery", "meets both"]].round(4).to_string())
print("excluded participants meeting both:", int(inc.loc[excl, "meets both"].sum()), "of", len(excl))
print("participants meeting both, over all 14:", list(inc.index[inc["meets both"]]))
         n which_trials  t pre-ket  p pre-ket (BH)  t post-ket  p post-ket (BH)  mean prei  mean infu  mean post  decrease  recovery  meets both
subject                                                                                                                                         
SD163                39    -0.3794          0.7065      1.7575           0.1738     0.6664     0.6786     0.7761     False     False       False
SD170                39     0.1429          0.8871     -1.1008           0.5558     0.1266     0.1207     0.0489     False     False       False
SD178                27    -1.7376          0.0941     -3.6936           0.0021     0.9330     0.9758     0.8988     False     False       False
SD184                30    -0.9389          0.3555      2.4481           0.0413     0.4019     0.4277     0.5015     False      True       False
SD207                11     4.8338          0.0014     -2.1669           0.0555     0.5165     0.2715     0.1720      True     False       False
SD215                39     1.2251          0.2281      2.3802           0.0448     0.3305     0.2075     0.4546     False      True       False
SD242                39     4.8557          0.0000     -0.2170           0.8294     0.7469     0.6285     0.6236      True     False       False
excluded participants meeting both: 0 of 7
participants meeting both, over all 14: ['SD168', 'SD172', 'SD181', 'SD185', 'SD186', 'SD211', 'SD245']

Result: reproduced. None of SD163, SD170, SD178, SD184, SD207, SD215 and SD242 meets both criteria, and the participants that do, over all 14, are the 7 of figs8a (printed list).

fig2c-ieeg-cohort-selective-affective-decrease¶

In the iEEG cohort, ketamine reduces the late (affective) phase of the eyepuff eye closure while the early reflexive phase remains.

Fig. 2C is the mean over the 7 participants of each participant's median trace over the trials of which_trials (code, overlay_sessions_eye_closure_traces_by_pt). Early and late means are taken on these median traces. The code plots them on a time axis shifted by 0.05 s (origin_shift_s); the early and late means are shown with the windows unshifted (the stored windows) and shifted 0.05 s later. The paired t-test of early eye closure, ketamine against preinfusion, is the criterion's.

In [16]:
rows = []
traces = {}
for sub in he.INCLUDED:
    b = beh[sub]
    for sess in he.SESSIONS:
        med = np.nanmedian(b[sess]["trials"][b["which_trials"]], axis=0)[None, :]
        traces[(sub, sess)] = med[0]
        for shift in [0.0, 0.05]:
            rows.append({"subject": sub, "session": sess, "shift": shift,
                         "early": he.window_mean(med, he.EARLY_W, shift)[0],
                         "late": he.window_mean(med, he.LATE_W, shift)[0]})
c2 = pd.DataFrame(rows)
for shift in [0.0, 0.05]:
    d = c2[c2["shift"] == shift]
    m = d.groupby("session")[["early", "late"]].agg(["mean", "sem"]).loc[he.SESSIONS]
    print(f"window shift {shift} s, mean (sem) over 7 participants:")
    print(m.round(3).to_string())
    e = d.pivot(index="subject", columns="session", values="early")
    l = d.pivot(index="subject", columns="session", values="late")
    te = st.ttest_rel(e.preinfusion, e.infusion)
    print(f"  early, pre against ketamine: paired t = {te.statistic:.3f}, p = {te.pvalue:.4f} (positive t = ketamine lower)")
    for other in ["preinfusion", "postinfusion"]:
        tl = st.ttest_rel(l[other], l.infusion)
        print(f"  late, {other} against ketamine: t = {tl.statistic:.3f}, p = {tl.pvalue:.4f}; ketamine lower in {(l.infusion < l[other]).sum()} of 7")
    print()
print("per participant (no shift):")
print(c2[c2["shift"] == 0].pivot(index="subject", columns="session", values=["early", "late"]).round(3).to_string())
window shift 0.0 s, mean (sem) over 7 participants:
              early          late       
               mean    sem   mean    sem
session                                 
preinfusion   0.877  0.010  0.541  0.082
infusion      0.850  0.024  0.306  0.087
postinfusion  0.875  0.015  0.518  0.082
  early, pre against ketamine: paired t = 1.318, p = 0.2356 (positive t = ketamine lower)
  late, preinfusion against ketamine: t = 2.908, p = 0.0271; ketamine lower in 7 of 7
  late, postinfusion against ketamine: t = 3.447, p = 0.0137; ketamine lower in 7 of 7

window shift 0.05 s, mean (sem) over 7 participants:
              early          late       
               mean    sem   mean    sem
session                                 
preinfusion   0.862  0.015  0.523  0.083
infusion      0.828  0.031  0.284  0.086
postinfusion  0.849  0.015  0.503  0.079
  early, pre against ketamine: paired t = 1.405, p = 0.2096 (positive t = ketamine lower)
  late, preinfusion against ketamine: t = 2.802, p = 0.0311; ketamine lower in 7 of 7
  late, postinfusion against ketamine: t = 3.712, p = 0.0099; ketamine lower in 7 of 7

per participant (no shift):
           early                              late                         
session infusion postinfusion preinfusion infusion postinfusion preinfusion
subject                                                                    
SD168      0.923        0.910       0.858    0.784        0.821       0.830
SD172      0.830        0.910       0.875    0.249        0.768       0.377
SD181      0.864        0.861       0.869    0.102        0.393       0.795
SD185      0.933        0.858       0.934    0.367        0.557       0.607
SD186      0.769        0.923       0.868    0.249        0.499       0.474
SD211      0.782        0.822       0.855    0.122        0.227       0.239
SD245      0.852        0.838       0.880    0.268        0.362       0.461
In [17]:
tt = (np.arange(800) - 400) / he.FPS
fig, ax = plt.subplots(figsize=(5, 3))
allv = []
for sess in he.SESSIONS:
    m = np.mean([traces[(s, sess)] for s in he.INCLUDED], axis=0)
    allv.append(m)
    ax.plot(tt, m, label=sess)
ax.set_xlim(-0.5, 1.5); ax.set_xlabel("time from puff (s)"); ax.set_ylabel("eye closure"); ax.legend()
plt.show()
sel = (tt >= -0.5) & (tt <= 1.5)
print("plotted range:", round(min(a[sel].min() for a in allv), 3), "to", round(max(a[sel].max() for a in allv), 3))
No description has been provided for this image
plotted range: 0.035 to 0.929

Result: reproduced. Across the 7 participants, mean late eye closure of the median traces is lower on ketamine than at preinfusion and at postinfusion, and early eye closure on ketamine is not significantly lower than at preinfusion by the paired t-test, with the windows unshifted and shifted by 0.05 s (numbers above).

fig2d-ieeg-cadss-reversible¶

In the 7 included iEEG participants, dissociation (CADSS) rises during ketamine and returns to baseline after infusion (0.86, 22.43, 0.86).

Scores from the code record (link above); infusion is the maximum over the time points up to 50 min, with SD185's retrospective rating in place of its infusion rating (claims file; code mean_cadss_by_sess). Paired t-tests with Benjamini-Hochberg over the two.

In [18]:
c7 = cad.loc[he.INCLUDED, ["pre", "infusion", "post", "retro"]].copy()
c7.loc["SD185", "infusion"] = c7.loc["SD185", "retro"]
print(c7.to_string())
t1 = st.ttest_rel(c7.pre, c7.infusion); t2 = st.ttest_rel(c7.post, c7.infusion)
pc = multipletests([t1.pvalue, t2.pvalue], method="fdr_bh")[1]
for col in ["pre", "infusion", "post"]:
    print(f"{col}: mean {c7[col].mean():.2f}, sem {st.sem(c7[col]):.2f}")
print(f"pre against ketamine t = {t1.statistic:.3f}, corrected p = {pc[0]:.4f}; post against ketamine t = {t2.statistic:.3f}, corrected p = {pc[1]:.4f}")
c7b = cad.loc[he.INCLUDED, ["pre", "infusion", "post"]]
print(f"variant without the retrospective rating for SD185: infusion mean {c7b.infusion.mean():.2f}")
print("expected: 0.86 +/- 0.46, 22.43 +/- 4.75, 0.86 +/- 0.46; paper t = 4.807 and 4.512, p = 0.004 and 0.004")
       pre  infusion  post  retro
pt                               
SD168    0       9.0   3.0    NaN
SD172    0      30.0   0.0    NaN
SD181    0      10.0   0.0    NaN
SD185    0      10.0   0.0   10.0
SD186    1      33.0   2.0   20.0
SD211    3      26.0   0.0   31.0
SD245    2      39.0   1.0   54.0
pre: mean 0.86, sem 0.46
infusion: mean 22.43, sem 4.75
post: mean 0.86, sem 0.46
pre against ketamine t = -4.807, corrected p = 0.0040; post against ketamine t = -4.513, corrected p = 0.0040
variant without the retrospective rating for SD185: infusion mean 21.00
expected: 0.86 +/- 0.46, 22.43 +/- 4.75, 0.86 +/- 0.46; paper t = 4.807 and 4.512, p = 0.004 and 0.004

Result: reproduced, on the code record's scores. CADSS is 0.86 before, 22.43 during and 0.86 after ketamine, with both corrected p = 0.004; these are the code record's numbers and nothing in the dataset bears on them.

figs14g-cadss-correlates-with-affective-decrease¶

Across participants, the ketamine increase in CADSS correlates with the ketamine reduction of normalised affective eye closure (Spearman 0.744, 10 participants).

Participants: the iEEG eyepuff participants with behaviour and CADSS (the dataset has no bolus participant), excluding those whose median per-trial late/early at preinfusion is below 0.5 (claims file; code load_cadss_and_behavior). CADSS change: infusion minus preinfusion total, SD185's retrospective rating as its infusion value. Eye-closure change, reading (a), the code's: mean of the preinfusion and postinfusion medians minus the infusion median; reading (b): preinfusion median minus infusion median.

In [19]:
rows = []
for sub in he.IEEG_SUBJECTS:
    b = beh[sub]
    med = {s: np.nanmedian(he.per_trial(b, s)["late_over_early"]) for s in he.SESSIONS}
    inf = cad.loc[sub, "retro"] if sub == "SD185" else cad.loc[sub, "infusion"]
    rows.append({"subject": sub, "median pre": med["preinfusion"], "median ket": med["infusion"],
                 "median post": med["postinfusion"],
                 "change (a)": (med["preinfusion"] + med["postinfusion"]) / 2 - med["infusion"],
                 "change (b)": med["preinfusion"] - med["infusion"],
                 "CADSS change": inf - cad.loc[sub, "pre"]})
g = pd.DataFrame(rows).set_index("subject")
g["kept"] = g["median pre"] >= 0.5
print(g.round(3).to_string())
k = g[g.kept]
print("participants kept:", len(k))
for r in ["change (a)", "change (b)"]:
    rho = st.spearmanr(k["CADSS change"], k[r])
    print(f"reading {r}: Spearman rho = {rho.statistic:.3f}, p = {rho.pvalue:.4f}")
rho_all = st.spearmanr(g["CADSS change"], g["change (a)"])
print(f"variant, all 14 participants, reading (a): rho = {rho_all.statistic:.3f}, p = {rho_all.pvalue:.4f}")
print("expected: rho 0.744 +/- 0.074, p = 0.014, 10 participants")
         median pre  median ket  median post  change (a)  change (b)  CADSS change   kept
subject                                                                                  
SD163         0.646       0.646        0.767       0.061       0.000           1.0   True
SD168         0.961       0.842        0.898       0.088       0.119           9.0   True
SD170         0.103      -0.002        0.037       0.072       0.105           4.0  False
SD172         0.509       0.348        0.781       0.297       0.161          30.0   True
SD178         0.942       0.993        0.917      -0.064      -0.051           2.0   True
SD181         0.961       0.193        0.530       0.552       0.768          10.0   True
SD184         0.395       0.413        0.488       0.029      -0.018          38.0  False
SD185         0.574       0.427        0.678       0.199       0.147          10.0   True
SD186         0.508       0.334        0.494       0.167       0.174          32.0   True
SD207         0.513       0.252        0.163       0.086       0.261           4.0   True
SD211         0.316       0.160        0.382       0.189       0.156          23.0  False
SD215         0.364       0.417        0.374      -0.048      -0.053          22.0  False
SD242         0.748       0.626        0.628       0.062       0.122           1.0   True
SD245         0.559       0.402        0.500       0.128       0.157          37.0   True
participants kept: 10
reading change (a): Spearman rho = 0.744, p = 0.0136
reading change (b): Spearman rho = 0.561, p = 0.0916
variant, all 14 participants, reading (a): rho = 0.355, p = 0.2129
expected: rho 0.744 +/- 0.074, p = 0.014, 10 participants

Result: reproduced under reading (a). Ten participants remain after the exclusion (SD170, SD184, SD211 and SD215 have preinfusion medians below 0.5), and with the code's eye-closure change the Spearman correlation with the CADSS change is 0.744, p = 0.0136, the paper's value. Under reading (b), ketamine minus preinfusion, rho is 0.561 and p = 0.0916, not significant. The CADSS values are the code record's.

Summary¶

Claim Outcome
p50-condition-labels-link reproduced (identity and counts; the saline label rests on the code)
cadss-scores-link reproduced
fig1e-human-late-eye-closure-ketamine reproduced
figs1b-human-early-eye-closure-unchanged reproduced
figs1c-human-late-eye-closure-decrease reproduced
figs1d-human-cadss-saline-ketamine reproduced (code record's scores)
fig1f-human-subjective-reports blocked, data missing
figs8a-ieeg-inclusion-per-participant reproduced
figs8b-ieeg-excluded-participants reproduced
fig2c-ieeg-cohort-selective-affective-decrease reproduced
fig2d-ieeg-cadss-reversible reproduced (code record's scores)
figs14g-cadss-correlates-with-affective-decrease reproduced under reading (a), the code's (code record's scores)

Differences from the paper's method: the eye-closure traces are taken as deposited (already normalised to the open eye); the eye sizes and the DeepLabCut tracking are not in these files. The saline label of the non-iEEG series and all CADSS scores come from the code record.

About the dataset: the non-iEEG files do not say which series is saline; the CADSS scores and the verbal reports are not deposited; the iEEG behaviour files are internally consistent (trial windows, stored per-trial values and their ratio agree with the continuous trace).