alterlab-pyhealth — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited alterlab-pyhealth (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
PyHealth is a Python library for healthcare AI that provides datasets, task definitions, models, trainers, and medical-code utilities for clinical machine learning. Use this skill when developing healthcare prediction models, processing clinical data, working with medical coding systems, or validating models before any clinical use.
Version gotcha (read first). This skill targets PyHealth 2.x (current release 2.0.2, Sept 2026). The 2.0 rewrite changed the API in ways most tutorials and pre-2025 snippets get wrong: - Tasks are classes you instantiate, e.g.MortalityPredictionMIMIC4(),DrugRecommendationMIMIC3()— not the old snake-casemortality_prediction_mimic4_fnfunctions. Pass the instance todataset.set_task(task). - Datasets take an explicit table list. Single-source loaders useroot=+tables=[...](MIMIC3Dataset,MIMIC4EHRDataset,eICUDataset,OMOPDataset); the multimodalMIMIC4Datasetusesehr_root=+ehr_tables=[...](plus optionalnote_root/cxr_root). - Models take only the `SampleDataset` plus hyperparameters, e.g.Transformer(dataset=samples, embedding_dim=128). Feature keys, label key, and mode are read from the task'sinput_schema/output_schema; the 1.xfeature_keys=/label_key=/mode=arguments raiseTypeError. - Metric names have no `_score` suffix:pr_auc,roc_auc,f1; multilabel/drug-rec use the*_samplesfamily (jaccard_samples,f1_samples,pr_auc_samples). Passmetrics=[...]to the `Trainer` constructor andmonitor=one of those names. - Checkpoints usetrainer.save_ckpt(path)/trainer.load_ckpt(path)(there is notrainer.save). - 2.0.2 requires Python 3.12 or 3.13 and pins its own stack (numpy 2.2, pandas 2.3, torch 2.7, transformers 4.53), so install it in a dedicated environment rather than next to pandas 3 / transformers 5.
>
When unsure of a class or argument name, check the installed source rather than trusting older snippets.
Invoke this skill when:
| Scenario | Use Instead |
|---|---|
| Cleaning a raw ECG/EEG/EDA trace and computing HRV or SCR features (no model training) | alterlab-neurokit2 |
| Reading, anonymizing, or converting DICOM image files | alterlab-pydicom |
| Kaplan-Meier / Cox time-to-event modeling on a tabular clinical dataset | alterlab-scikit-survival |
| Biomarker-stratified cohort report with GRADE-graded treatment recommendations | alterlab-clinical-decision |
| General tabular ML on non-EHR data (scikit-learn pipelines) | alterlab-scikit-learn |
PyHealth operates through a modular 5-stage pipeline:
BaseTask subclassesTrainer with best-checkpoint selection, monitoring, and evaluationPyHealth 2.x uses a polars-backed data layer and caches task samples, which keeps large EHR tables memory-efficient.
from pyhealth.datasets import MIMIC4EHRDataset, split_by_patient, get_dataloader
from pyhealth.tasks import MortalityPredictionMIMIC4
from pyhealth.models import Transformer
from pyhealth.trainer import Trainer
# 1. Load dataset (declare the tables the task needs) and set the task (a class instance)
dataset = MIMIC4EHRDataset(
root="/path/to/mimic-iv/2.2",
tables=["diagnoses_icd", "procedures_icd", "prescriptions"],
)
sample_dataset = dataset.set_task(MortalityPredictionMIMIC4())
# 2. Split data by patient (no leakage across splits)
train, val, test = split_by_patient(sample_dataset, [0.7, 0.1, 0.2], seed=42)
# 3. Create data loaders
train_loader = get_dataloader(train, batch_size=64, shuffle=True)
val_loader = get_dataloader(val, batch_size=64, shuffle=False)
test_loader = get_dataloader(test, batch_size=64, shuffle=False)
# 4. Initialize the model: inputs, label ("mortality"), and mode ("binary")
# all come from the task schema, so only hyperparameters are passed
model = Transformer(dataset=sample_dataset, embedding_dim=128)
trainer = Trainer(model=model, metrics=["pr_auc", "roc_auc", "f1"]) # device auto-detected
trainer.train(
train_dataloader=train_loader,
val_dataloader=val_loader,
epochs=50,
monitor="pr_auc", # AUPRC — robust for the rare-mortality class
monitor_criterion="max",
)
# 5. Evaluate (uses the metrics passed to the Trainer)
results = trainer.evaluate(test_loader)Read the reference file that matches the step you are on:
| File | Read when | Key topics |
|---|---|---|
references/datasets.md | Loading MIMIC/eICU/OMOP/signal datasets, splitting data | Patient/Event structures, loaders, split_by_patient / split_by_visit / split_by_sample |
references/medical_coding.md | Translating or grouping ICD, NDC, RxNorm, ATC, CCS codes | InnerMap lookups and hierarchy, CrossMap translation |
references/tasks.md | Choosing a predefined task or writing a custom one | 2.x task classes, input_schema / output_schema, custom BaseTask |
references/models.md | Selecting and configuring a model | Baselines, RNN/CNN/Transformer, RETAIN, SafeDrug, GAMENet, StageNet, GAT/GCN |
references/preprocessing.md | Understanding how raw events become tensors | Schema-string processors ("sequence", "timeseries", "binary", ...) |
references/training_evaluation.md | Training, metrics, calibration, uncertainty, interpretability | Trainer, metric strings, conformal prediction, Chefer/IG attributions |
uv venv --python 3.13 .venv-pyhealth # PyHealth 2.0.2 supports Python 3.12-3.13
source .venv-pyhealth/bin/activate
uv pip install "pyhealth>=2.0.2"Requirements (PyHealth 2.0.2):
>=3.12,<3.14) — if your default interpreter is 3.14, create the environment with --python 3.13.2.1 alpha line exists on PyPI; stay on the 2.0.x releases unless you need an alpha-only feature.Objective: Predict patient mortality in intensive care unit
Approach:
references/datasets.mdreferences/tasks.mdreferences/models.mdreferences/training_evaluation.mdreferences/training_evaluation.mdObjective: Recommend medications while avoiding drug-drug interactions
Approach:
references/datasets.mdDrugRecommendation* task → Read references/tasks.mdreferences/models.mdreferences/medical_coding.mdjaccard_samples, f1_samples, pr_auc_samples) → Read references/training_evaluation.mdObjective: Identify patients at risk of readmission
Approach:
references/datasets.mdReadmissionPrediction* task → Read references/tasks.mdreferences/training_evaluation.mdreferences/models.mdreferences/training_evaluation.mdObjective: Classify sleep stages from EEG signals
Approach:
references/datasets.mdSleepStagingSleepEDF or the legacy sleep_staging_*_fn functions) → Read references/tasks.mdreferences/preprocessing.mdreferences/models.mdf1_macro, cohen_kappa) → Read references/training_evaluation.mdObjective: Standardize diagnoses across different coding systems
Approach:
references/medical_coding.md for comprehensive guidanceCrossMap to translate between ICD-9, ICD-10, and CCSObjective: Automatically assign ICD codes from clinical notes
Approach:
references/datasets.mdMIMIC3ICD9Coding task → Read references/tasks.mdreferences/preprocessing.mdTransformersModel(dataset=..., model_name="emilyalsentzer/Bio_ClinicalBERT") → Read references/models.mdreferences/training_evaluation.md from pyhealth.datasets import split_by_patient
train, val, test = split_by_patient(sample_dataset, [0.7, 0.1, 0.2], seed=42) dataset.stats() # prints patient and event counts (returns None)references/preprocessing.md)LogisticRegression for binary/multi-class tasksMLP for an initial deep learning baselineTrainer(metrics=[...]) / monitor=):roc_auc, pr_auc (prefer pr_auc for rare events), f1, accuracyf1_macro, f1_weighted, accuracy, cohen_kappajaccard_samples, f1_samples, pr_auc_samples, ddi (reported as ddi_score)mae, mse, kl_divergencereferences/training_evaluation.md)`TypeError: ... unexpected keyword argument 'feature_keys'` (or `'root'`):
dataset= and hyperparameters to models; use MIMIC4EHRDataset(root=..., tables=...) or MIMIC4Dataset(ehr_root=..., ehr_tables=...)ImportError or missing tables:
Out of memory:
max_seq_len on Transformer)dev=True to the dataset loader (e.g. MIMIC4EHRDataset(..., dev=True)) to prototype on the first 1,000 patientsPoor performance:
pr_auc vs roc_auc)Slow training:
Trainer(..., device="cuda"))# Complete mortality prediction pipeline (PyHealth 2.0.x)
import torch
from pyhealth.datasets import MIMIC4EHRDataset, split_by_patient, get_dataloader
from pyhealth.tasks import MortalityPredictionMIMIC4
from pyhealth.models import Transformer
from pyhealth.trainer import Trainer
from pyhealth.interpret.methods import CheferRelevance
# 1. Load dataset (declare the tables the task needs)
dataset = MIMIC4EHRDataset(
root="/data/mimic-iv/2.2",
tables=["diagnoses_icd", "procedures_icd", "prescriptions"],
)
dataset.stats()
# 2. Define task (instantiate the task class)
sample_dataset = dataset.set_task(MortalityPredictionMIMIC4())
print(f"Generated {len(sample_dataset)} samples")
# 3. Split data (by patient to prevent leakage)
train_ds, val_ds, test_ds = split_by_patient(sample_dataset, [0.7, 0.1, 0.2], seed=42)
# 4. Create data loaders
train_loader = get_dataloader(train_ds, batch_size=64, shuffle=True)
val_loader = get_dataloader(val_ds, batch_size=64)
test_loader = get_dataloader(test_ds, batch_size=64)
# 5. Initialize the model (schema-driven; swap in RETAIN(dataset=sample_dataset,
# embedding_dim=128) for a model that is interpretable by design)
model = Transformer(dataset=sample_dataset, embedding_dim=128, heads=2, num_layers=2)
# 6. Train, keeping the best checkpoint by validation AUPRC
trainer = Trainer(model=model, metrics=["accuracy", "pr_auc", "roc_auc", "f1"])
trainer.train(
train_dataloader=train_loader,
val_dataloader=val_loader,
epochs=50,
optimizer_class=torch.optim.Adam,
optimizer_params={"lr": 1e-3},
weight_decay=1e-5,
monitor="pr_auc", # AUPRC for the imbalanced (rare-mortality) outcome
monitor_criterion="max",
patience=5, # early stopping
)
# 7. Evaluate on the test set (uses the metrics passed to the Trainer)
for metric, value in trainer.evaluate(test_loader).items():
print(f" {metric}: {value:.4f}")
# 8. Predictions with patient IDs: inference() returns (y_true, y_prob, loss),
# extended with patient_ids when return_patient_ids=True
y_true, y_prob, loss, patient_ids = trainer.inference(test_loader, return_patient_ids=True)
positive_prob = y_prob if y_prob.ndim == 1 else y_prob[..., -1]
high_risk_idx = int(positive_prob.argmax())
print(f"Highest-risk patient: {patient_ids[high_risk_idx]} ({float(positive_prob[high_risk_idx]):.3f})")
# 9. Token-level relevance (Chefer; supported by Transformer and StageAttentionNet)
relevance = CheferRelevance(model)
batch = next(iter(get_dataloader(test_ds, batch_size=1, shuffle=False)))
for feature_key, rel in relevance.attribute(**batch).items():
print(f"{feature_key}: top tokens -> {rel[0].topk(min(5, rel.shape[-1])).indices.tolist()}")
# 10. Save the trained weights
trainer.save_ckpt("./models/mortality_transformer.pt")For detailed information on each component, see the reference files in references/: datasets.md, medical_coding.md, tasks.md, models.md, preprocessing.md, and training_evaluation.md (see the table under Detailed Documentation for when to read each).
Part of the AlterLab Academic Skills suite.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.