Source code for scitex_seizure_metrics.papers.cook2013
"""Cook et al. 2013 (Lancet Neurology) replica metrics.
Paper: 'Prediction of seizure likelihood with a long-term, implanted
seizure advisory system in patients with drug-resistant epilepsy: a
first-in-man study'. The clinical-trial seminal paper. Reports:
- per-patient sensitivity at high-likelihood warning windows.
- proportion of time in high / moderate / low warning (Snyder scheme).
- no AUROC / AUPRC / FP/hr — clinical, not ML, framing.
Citation: Cook MJ et al., Lancet Neurol 2013; 12: 563–571.
doi:10.1016/S1474-4422(13)70075-9
"""
from __future__ import annotations
import numpy as np
from .. import forecasting
from ..policy import AlarmPolicy
[docs]
def metrics(*, y_proba, times_seconds, seizure_times,
sph_seconds: float = 0.0,
sop_seconds: float = 60 * 60,
high_threshold: float = 0.8,
moderate_threshold: float = 0.5,
n_surrogate: int = 200,
name: str = "cook2013") -> dict:
"""Reproduce the Snyder-scheme time-in-warning panel.
Args:
y_proba: per-window predicted probabilities.
times_seconds: matching timestamps.
seizure_times: seizure onset times.
sph_seconds: prediction horizon.
sop_seconds: occurrence period.
high_threshold / moderate_threshold: lights cut-offs.
n_surrogate: surrogates for IoC.
name: identifier.
Returns:
dict with sensitivity_high (the warning that flagged the seizure),
time_in_high_frac, time_in_moderate_frac, time_in_low_frac,
ioc_high — mirroring the Cook 2013 traffic-light reporting.
"""
y_proba = np.asarray(y_proba, dtype=float)
times_seconds = np.asarray(times_seconds, dtype=float)
cadence = float(np.median(np.diff(times_seconds))) if len(times_seconds) > 1 else 60.0
# High-likelihood evaluation
pol_high = AlarmPolicy(
sph_seconds=sph_seconds, sop_seconds=sop_seconds,
cadence_seconds=cadence, refractory_seconds=sop_seconds,
alarm_threshold=high_threshold, fp_denominator="interictal",
)
rep_high = forecasting.evaluate_stream(
y_proba, times_seconds, seizure_times, pol_high,
n_surrogate=n_surrogate, surrogate="poisson", name=name,
)
# Time-in-each-band fractions (recording-time-weighted)
above_high = (y_proba >= high_threshold).mean()
above_moderate = (
(y_proba >= moderate_threshold) & (y_proba < high_threshold)
).mean()
below_moderate = (y_proba < moderate_threshold).mean()
return {
"paper": "cook2013",
"name": name,
"sensitivity_high": rep_high.sensitivity,
"ioc_high": rep_high.ioc,
"fp_per_hour_high": rep_high.fp_per_hour,
"time_in_high_frac": float(above_high),
"time_in_moderate_frac": float(above_moderate),
"time_in_low_frac": float(below_moderate),
}