From 23f76e8e5f0d83b08e5f42fbb9c26666003ea40b Mon Sep 17 00:00:00 2001 From: dadudu13 Date: Mon, 5 Oct 2026 14:49:22 -0500 Subject: [PATCH 1/5] Add the evaluation package: configuration, sequences, model I/O, metrics The training and evaluation code behind the project's results lived in a private analysis repository, mixed with cohort-specific paths and column names. This ports the reusable part into evaluation/, with every path and column name a setting: - config.py: Columns (names of the id, date, visit, time, event and optional categorical columns, loadable from JSON) and Paths (data root, runs root, output dir, file-name templates), each defaulting to an environment variable. - data.py: the three views a per-visit table is scored under -- one sample per patient (last visit), one per visit, one per fixed landmark day -- and build_landmark_table, which adds forward-filled snapshot rows for patients still at risk on each landmark day. - model.py: build / train / save / load a split's model with its full architecture and objective in metadata.json, and predictions conditioned on the landmark, F_k(s + h | T > s). - metrics.py: Aalen-Johansen incidence, Graf IPCW weights, the IPCW cumulative/dynamic AUC with a competing event (cases by h weighted by 1/G(T-), controls event-free after h), Brier/IPA, calibration slope and intercept, top-decile O:E, patient bootstrap, Wilcoxon, BH. The functions are ports, not rewrites: on the original cohort they reproduce that repository's saved predictions to within 1e-16 and its metric tables to within floating-point rounding. tests/test_evaluation_metrics.py checks the metrics on hand-computed cases, Aalen-Johansen against scikit-survival, and the AUC against R timeROC (identical apart from timeROC excluding events that fall exactly on day h). The slope fit returns NaN instead of failing when every prediction is equal (statsmodels drops a constant regressor), which a coarse comparator score can produce. --- evaluation/__init__.py | 9 + evaluation/config.py | 158 ++++++++++++++++ evaluation/data.py | 268 +++++++++++++++++++++++++++ evaluation/metrics.py | 209 +++++++++++++++++++++ evaluation/model.py | 299 +++++++++++++++++++++++++++++++ pyproject.toml | 4 +- tests/test_evaluation_metrics.py | 145 +++++++++++++++ 7 files changed, 1091 insertions(+), 1 deletion(-) create mode 100644 evaluation/__init__.py create mode 100644 evaluation/config.py create mode 100644 evaluation/data.py create mode 100644 evaluation/metrics.py create mode 100644 evaluation/model.py create mode 100644 tests/test_evaluation_metrics.py diff --git a/evaluation/__init__.py b/evaluation/__init__.py new file mode 100644 index 0000000..6f106cc --- /dev/null +++ b/evaluation/__init__.py @@ -0,0 +1,9 @@ +"""How the landmark Dynamic-DeepHit models are trained and evaluated. + +Scripts (run with python -m evaluation.; each has --help): + make_synthetic a synthetic cohort in the expected layout + train train one configuration on every split + apply_external score an external / temporal cohort with each split's model + evaluate compare configurations on landmarks, visits and the external cohort + history_ablation within-patient test of whether earlier visits are used +""" diff --git a/evaluation/config.py b/evaluation/config.py new file mode 100644 index 0000000..f0426cc --- /dev/null +++ b/evaluation/config.py @@ -0,0 +1,158 @@ +"""Where the data lives and what its columns are called. + +Nothing in the evaluation scripts names a file or a column directly. Every path +comes from a command-line flag or, when the flag is omitted, an environment +variable; every column name comes from a Columns mapping, which defaults to the +generic names below and can be replaced with a JSON file (--columns or +DDH_COLUMNS). Patient-level data therefore stays wherever its owner keeps it. + +Layout under the data root, one folder per random split: + + /split_/