Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api/datasets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ Available Datasets
datasets/pyhealth.datasets.ChestXray14Dataset
datasets/pyhealth.datasets.PhysioNetDeIDDataset
datasets/pyhealth.datasets.EEGBCIDataset
PTB-XL Dataset <datasets/pyhealth.datasets.PTBXLDataset>
datasets/pyhealth.datasets.TUABDataset
datasets/pyhealth.datasets.TUEVDataset
datasets/pyhealth.datasets.ClinVarDataset
Expand Down
34 changes: 34 additions & 0 deletions docs/api/datasets/pyhealth.datasets.PTBXLDataset.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
pyhealth.datasets.PTBXLDataset
==============================

Overview
--------

PTB-XL is a large publicly available 12-lead ECG dataset from PhysioNet
(version 1.0.3). It contains **21,799** clinical ECG records of 10 seconds
from **18,869** patients, with multi-label SCP-ECG statements, official
stratified folds (``strat_fold``), and demographic / site / device metadata
suited to shift-aware evaluation. Only v1.0.3 is supported (v1.0.1 figures
of 21,837 records / 18,885 patients come from Wagner et al., Scientific
Data 2020, and must not be mixed with the PhysioNet v1.0.3 counts).

For more information see `PhysioNet PTB-XL v1.0.3
<https://physionet.org/content/ptb-xl/1.0.3/>`_ and Wagner et al.,
`Scientific Data 2020 <https://www.nature.com/articles/s41597-020-0495-6>`_.

Optional dependency: install waveform I/O with ``pip install 'pyhealth[ptbxl]'``.

``PTBXLDataset.root`` is the version directory passed to the constructor.
Derived metadata CSVs are written under ``metadata_cache_dir`` (default
``~/.cache/pyhealth/datasets/ptbxl/``), not into ``root``, so read-only
data mounts stay untouched. ``BaseDataset.load_table`` concatenates
``root / file_path`` and cannot take an absolute path; PTB-XL overrides
``load_table`` to read the derived CSV from the metadata cache.

API Reference
-------------

.. autoclass:: pyhealth.datasets.PTBXLDataset
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/api/tasks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ Available Tasks
Sleep Staging (SleepEDF) <tasks/pyhealth.tasks.SleepStagingSleepEDF>
Temple University EEG Tasks <tasks/pyhealth.tasks.temple_university_EEG_tasks>
EEGBCI Tasks <tasks/pyhealth.tasks.eegbci>
PTB-XL Tasks <tasks/pyhealth.tasks.ptbxl>
Sleep Staging v2 <tasks/pyhealth.tasks.sleep_staging_v2>
Benchmark EHRShot <tasks/pyhealth.tasks.benchmark_ehrshot>
ChestX-ray14 Binary Classification <tasks/pyhealth.tasks.ChestXray14BinaryClassification>
Expand Down
24 changes: 24 additions & 0 deletions docs/api/tasks/pyhealth.tasks.ptbxl.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pyhealth.tasks.ptbxl
====================

Overview
--------

Task helpers for PTB-XL multi-label ECG diagnosis. This module currently
implements the official **5-diagnostic-superclass** classification task
(``NORM``, ``MI``, ``STTC``, ``CD``, ``HYP``) by aggregating diagnostic SCP
statements via ``scp_statements.csv``.

Empty superclass label sets (≈400 records on v1.0.1, Wagner et al.
Table 9; mainly pacemaker ECGs) are dropped by default. On the full
v1.0.3 corpus, ``MultiLabelProcessor`` emits labels in alphabetical
order: ``CD, HYP, MI, NORM, STTC``. Official fold splitting is provided
by :func:`pyhealth.datasets.split_by_strat_fold`.

API Reference
-------------

.. automodule:: pyhealth.tasks.ptbxl
:members:
:undoc-members:
:show-inheritance:
77 changes: 77 additions & 0 deletions examples/ecg/ptbxl/ptbxl_superclass_quickstart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""PTB-XL 5-superclass multi-label quickstart.

Requires::

pip install 'pyhealth[ptbxl]'

Download PTB-XL v1.0.3 from https://physionet.org/content/ptb-xl/1.0.3/
and point ``--root`` at the extracted version directory (contains
``ptbxl_database.csv``, ``scp_statements.csv``, ``records100/``).

Example::

python examples/ecg/ptbxl/ptbxl_superclass_quickstart.py \\
--root /data/ptb-xl/1.0.3 --dev
"""

from __future__ import annotations

import argparse
from pathlib import Path

from pyhealth.datasets import PTBXLDataset, split_by_strat_fold
from pyhealth.tasks import PTBXLSuperclassClassification


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--root",
type=Path,
required=True,
help="PTB-XL version root (ptbxl_database.csv + records*/)",
)
parser.add_argument(
"--sampling-rate",
type=int,
default=100,
choices=(100, 500),
help="Waveform sampling rate (default: 100 Hz / filename_lr)",
)
parser.add_argument(
"--dev",
action="store_true",
help="Limit patients via BaseDataset.dev mode",
)
args = parser.parse_args()

dataset = PTBXLDataset(
root=str(args.root),
sampling_rate=args.sampling_rate,
dev=args.dev,
)
task = PTBXLSuperclassClassification(
scp_statements_path=str(args.root / "scp_statements.csv"),
)
samples = dataset.set_task(task)
train, val, test = split_by_strat_fold(samples)
print(
f"samples={len(samples)} "
f"train={len(train)} val={len(val)} test={len(test)}"
)
if len(samples):
first = samples[0]
print(
"first sample:",
{
"patient_id": first["patient_id"],
"record_id": first["record_id"],
"labels": first["labels"],
"strat_fold": first["strat_fold"],
"signal_shape": tuple(first["signal"].shape),
},
)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions pyhealth/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ def __init__(self, *args, **kwargs):
split_by_sample_conformal,
split_by_sample_conformal_tuh,
split_by_sample_tuh,
split_by_strat_fold as split_by_strat_fold,
split_by_visit,
split_by_visit_conformal,
)
from .eegbci import EEGBCIDataset as EEGBCIDataset # noqa: E402
from .ptbxl import PTBXLDataset as PTBXLDataset
from .tuab import TUABDataset
from .tuev import TUEVDataset
from .utils import (
Expand Down
22 changes: 22 additions & 0 deletions pyhealth/datasets/configs/ptbxl.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: "1.0.3" # PhysioNet PTB-XL v1.0.3 only; earlier releases are not supported.
tables:
records:
# Template only: PTBXLDataset writes a resolved config whose file_path
# is the derived cache CSV (rate + data-root hash) before BaseDataset init.
file_path: "ptbxl-pyhealth-100hz.csv"
patient_id: "patient_id"
# recording_date from ptbxl_database.csv (e.g. 1984-11-09 09:17:34)
timestamp: "recording_date"
timestamp_format: "%Y-%m-%d %H:%M:%S"
attributes:
- "record_id"
- "signal_file"
- "sampling_rate"
- "strat_fold"
- "age"
- "age_is_censored"
- "age_is_missing"
- "sex" # 0 = female, 1 = male
- "site"
- "device"
- "scp_codes"
Loading
Loading