Skip to content

PyHealth 2.1.0

Latest

Choose a tag to compare

@jhnwu3 jhnwu3 released this 16 Oct 23:46
· 5 commits to master since this release
e5394d5

PyHealth 2.1.0 focuses on making results trustworthy: leakage-free preprocessing with split-aware set_task, reproducible training, and calibration metrics with patient-level confidence intervals. It also adds an XGBoost model with exact TreeSHAP, Synthea synthetic-patient generation, ICD-9 ↔ ICD-10 translation, and a nine-notebook tutorial series.

pip install -U pyhealth==2.1.0
pip install -U "pyhealth[xgboost]==2.1.0"   # with XGBoostModel

Warning

Rebuild caches built with more than 10 workers. Before this release, set_task with more than 10 workers could misorder its sample index, so patient-level splits shared patients between train and val/test (on full MIMIC-IV with 32 workers, ~3.6k patients). 2.1.0 fixes this, but caches built that way are still reused as-is. Delete them and rebuild. (#1273)

Before you upgrade

  • Dependencies:
    • Requires torch>=2.10, pyarrow>=23.0.1, dask[complete]>=2026.1.0 and litdata>=0.2.64,<0.2.68. (#1254, #1273)
    • numpy, pandas, pydantic, scikit-learn and narwhals use bounded ranges, so installs on Google Colab no longer downgrade packages it has already loaded. (#1287)
    • icd-mappings is a new dependency; urllib3 is no longer one. (#1257, #1254)
  • EHR-generation tasks: the EHRGeneration base class is removed and there is one task per encoding and dataset (#1233):
    • EHRGenerationMIMIC3/4: per-visit multi-hot rows, for HALO. These now emit nested_multihot instead of nested_sequence.
    • EHRSequenceGenerationMIMIC3/4: per-visit code indices, for GPT-2 and PromptEHR.
    • EHRCodeSetGenerationMIMIC3/4: one code set per patient, for MedGAN and CorGAN.
  • Event timestamps: a missing timestamp is now None. It used to be datetime.now(), which sorted before every real event and slipped through time-window filters. (#1247)
  • Caches: dataset and sample caches rebuild once on first use, because their cache keys changed. Old cache folders stay on disk and can be deleted. (#1265, #1277)
  • Shuffles and splits: runs are now reproducible from a seed, but the same seed selects different batches and patients than in 2.0.2. (#1280, #1286)
  • Logging: the pyhealth logger no longer propagates to the root logger, so each message prints once in Jupyter and Colab. (#1285)
  • Default cache location: building a dataset without cache_dir logs a warning naming the folder it writes to. Set PYHEALTH_REQUIRE_CACHE_DIR=1 to make it an error. (#1264)
  • Removed: the unmaintained 2023 chat-assistant/ demo. (#1252)

New features

  • Split-aware preprocessing (#1273):
    • dataset.set_task(task, split=PatientSplit(...)) fits every processor on the training patients only and returns (train, val, test) views of one processed cache.
    • PatientSplit takes two or more ratios, for example train/val/calibration/test.
    • Custom splits implement the new Split interface.
    • Processors that learn statistics warn when they are fitted on all samples.
  • Reproducible training (#1280):
    • get_dataloader(..., seed=...) gives each loader its own seeded, per-epoch shuffle, in memory and on disk.
    • torch.manual_seed alone now makes runs repeatable.
    • New pyhealth.utils.preserve_rng_state(). set_task, dataset creation and the interpretability methods no longer change global random state, and importing pyhealth.models no longer reseeds numpy and torch.
    • Trainer.train restores the best epoch even with logging off (keep_best_in_memory).
  • Calibration and confidence intervals (#1268):
    • binary_metrics_fn adds brier, oe_ratio, calibration_slope and calibration_intercept.
    • New pyhealth.metrics.bootstrap with bootstrap_ci and paired_bootstrap_diff. Pass groups= to resample whole patients.
  • Imbalanced outcomes: BaseModel.set_pos_weight(...) takes a number, one number per label, or "balanced" computed from a training dataset. (#1267)
  • XGBoostModel (#1278):
    • Gradient-boosted trees for binary, multiclass, multilabel and regression tasks.
    • Train with model.fit(train, val=None), then evaluate with the usual Trainer.
    • Accepts tensor, timeseries, multi-hot and nested multi-hot inputs; bag_of_codes=True turns code sequences into per-patient code counts.
    • Supports scale_pos_weight and early stopping.
    • Exact TreeSHAP through explain() and the new pyhealth.interpret.methods.TreeSHAP.
  • Synthea (#1244):
    • pyhealth.models.Synthea runs the Synthea synthetic-patient generator. It needs Java 17+ and downloads and caches the Synthea JAR.
    • SyntheaCSVDataset loads Synthea's CSV output.
  • Medical codes (#1257):
    • CrossMap translates ICD-9 ↔ ICD-10 and maps codes to CCSR categories, chapters and blocks, and chronic-condition indicators, using the icd-mappings package.
    • Existing mappings such as ICD9CM → CCSCM are unchanged.
    • ICD-9 ↔ ICD-10 follows each code's primary mapping only, so some codes map to nothing or don't round-trip. Those are reported through a warning and CrossMap.unmapped_codes.
  • Processors:
    • New NestedMultiHotProcessor ("nested_multihot"): one multi-hot row per visit, 8.6× smaller per patient on eICU. (#1233)
    • TensorProcessor.size() returns the fitted feature width. (#1262)
  • HALO: visit encoding is vectorised, removing a loop that took ~85% of each training step on GPU. (#1233)

Fixes

  • Leakage and labels:
    • MultimodalMortalityPredictionMIMIC4 used chest X-rays taken after the prediction time. (#1229)
    • T1DDKAPredictionMIMIC4 counted DKA events recorded before a separate T1DM diagnosis. (#1230)
    • Patient-level splits with more than 10 workers mixed patients across parts (see the warning above). (#1273)
    • Supplying fitted processors for only some fields left the others unprocessed. (#1273)
  • Datasets and caching:
    • Editing a source file or the dataset's YAML config reused the stale cache. (#1265)
    • Sample fields whose length varies between samples failed or were misread on the disk cache. (#1277)
    • Visits longer than the fitted width produced batches with mismatched widths; they are now truncated with a warning. (#1263)
    • Event objects could not be copied or unpickled. (#1247)
    • Each dataset build leaked file descriptors, causing "Too many open files" on macOS. (#1245)
    • Interrupted downloads left truncated files that later runs reused, and stalled downloads hung. (#1255)
    • Patient splitters chose different patients for the same seed depending on sample order. (#1286)
  • Models:
    • 16 models crashed in Trainer.evaluate when output_schema used a processor class or tuple. (#1246, fixes #914)
    • GRASP never built its similar-patient graph. (#1194)
    • TransformerDeID ignored its configured feature and label keys. (#1224)
  • Interpretability: comprehensiveness and sufficiency crashed for every model with code-sequence inputs. (#1284)

Docs, examples and infrastructure

  • Nine-notebook tutorial series under examples/tutorials/series/, from a first model to contributing, each runnable on a CPU Colab runtime. (#1281)
  • New docs sections and example scripts for reproducible runs, split-aware set_task, reusing fitted processors, imbalanced outcomes, calibration and bootstrap CIs, cache behaviour, identified data, and logging setup.
  • SECURITY.md describes private vulnerability reporting and which files must come from trusted sources. (#1253)
  • Dependency minimums were raised past known security issues; pip-audit reports none on a fresh install. (#1254)
  • CI now reports test coverage. (#1299)

See CHANGELOG.md for full details on every change.

Full Changelog: v2.0.2...v2.1.0