Source code for scitex_seizure_metrics.papers.andrade2024

"""Andrade et al. 2024 (Front Neurosci) replica metrics.

Paper: 'On the performance of seizure prediction machine learning
methods across different databases: the sample and alarm-based
perspectives'. Reports both regimes side-by-side and a surrogate-
predictor chance baseline.

Citation: Andrade I, Teixeira C, Pinto M; Front Neurosci 2024;
doi:10.3389/fnins.2024.1417748
"""
from __future__ import annotations

import numpy as np

from .. import detection, forecasting
from ..policy import AlarmPolicy


[docs] def metrics(*, y_true, y_proba, times_seconds, seizure_times, sph_seconds: float = 5 * 60, sop_seconds: float = 30 * 60, n_surrogate: int = 1000, name: str = "andrade2024") -> dict: """Reproduce the side-by-side sample- + alarm-based panel. Returns: dict with sample_* and alarm_* metric pairs, plus the improvement-over-surrogate flag (analogous to the paper's '6/46 patients beat chance under alarm-based' framing). """ out = {"paper": "andrade2024", "name": name} # Sample-based rep_s = detection.evaluate(y_true, y_proba, threshold=0.5, name=name) out["sample_auroc"] = rep_s.roc_auc out["sample_pr_auc"] = rep_s.pr_auc out["sample_sensitivity"] = rep_s.sensitivity out["sample_specificity"] = ( None if rep_s.precision is None else 1.0 - rep_s.fp_per_day / 86400.0 ) out["sample_mcc"] = rep_s.mcc # Alarm-based cadence = float(np.median(np.diff(times_seconds))) if len(times_seconds) > 1 else 60.0 policy = AlarmPolicy( sph_seconds=sph_seconds, sop_seconds=sop_seconds, cadence_seconds=cadence, refractory_seconds=sop_seconds, alarm_threshold=0.5, fp_denominator="interictal", ) rep_a = forecasting.evaluate_stream( y_proba, times_seconds, seizure_times, policy, n_surrogate=n_surrogate, surrogate="poisson", name=name, ) out["alarm_sensitivity"] = rep_a.sensitivity out["alarm_fp_per_hour"] = rep_a.fp_per_hour out["alarm_time_in_warning_frac"] = rep_a.time_in_warning_frac out["alarm_ioc"] = rep_a.ioc out["beats_chance_alarm"] = bool(rep_a.ioc > 0) return out