Open-source Python library for AASM 2.6-compliant automated polysomnography scoring
Project description
psgscoring
Open-source AASM 2.6–compliant respiratory scoring for polysomnography.
Paper
Rombaut B, Rombaut B, Rombaut C, et al. Automated Polysomnography Scoring for Clinical Sleep Medicine: An Open-Source Platform Validated Against 59 Independent Scorer Sessions on PSG-IPA. Manuscript in preparation, 2026.
Technical details (signal processing chain, classification logic, all twelve bias corrections): Technical Reference (Online Supplement)
What this library does
psgscoring detects and classifies respiratory events (apneas, hypopneas, RERAs) in polysomnography recordings following AASM 2.6 rules. It extends YASA (Vallat & Walker, eLife 2021) from sleep staging into a complete clinical respiratory scoring pipeline.
Three contributions that distinguish this library:
- Twelve bias corrections — the first systematic identification and empirical quantification of six over-counting and six under-counting mechanisms in automated respiratory scoring
- AHI confidence interval — every study is scored at three stringency levels (strict/standard/sensitive), yielding a per-study robustness grade (A/B/C) rather than a single AHI number
- Clinical auditability — every event carries a confidence score, classification rule index, and per-correction counters, enabling the reviewing physician to verify individual scoring decisions
Installation
pip install psgscoring
Requirements: Python ≥3.9, numpy, scipy, mne. No GPU required.
Quick Start
import mne
from psgscoring import run_pneumo_analysis
# Load EDF and provide a hypnogram (e.g., from YASA)
raw = mne.io.read_raw_edf("recording.edf", preload=True)
hypnogram = ["W", "N1", "N2", "N2", "N3", ...] # per 30-s epoch
# Run the full pipeline
results = run_pneumo_analysis(raw, hypnogram, scoring_profile="aasm_v3_rec")
# Access results
resp = results["respiratory"]["summary"]
print(f"AHI: {resp['ahi_total']}, Severity: {resp['severity']}")
print(f"Events: {resp['n_obstructive']} OA, {resp['n_hypopnea']} Hyp")
# AHI confidence interval
interval = results["ahi_interval"]
print(f"AHI interval: [{interval['strict']['ahi']}–{interval['sensitive']['ahi']}]")
print(f"Robustness: {interval['robustness_grade']}")
Scoring Profiles
| Parameter | Strict | Standard | Sensitive |
|---|---|---|---|
| Hypopnea threshold | ≥30% | ≥30% | ≥25% |
| SpO₂ nadir window | 30 s | 45 s | 45 s |
| Peak-based detection | No | Yes | Yes |
Validation
PSG-IPA (PhysioNet): 5 recordings, 59 independent scorer sessions. Mean |ΔAHI| = 1.8/h, Pearson r = 0.997, severity concordance 4/5 (standard profile). See the paper for full results.
PSG-Audio (Sismanoglio Hospital, Athens): n=194, open access. External validation in progress.
Twelve Bias Corrections
| # | Correction | Direction | Clinical impact |
|---|---|---|---|
| 1 | Post-apnea baseline inflation | Over-counting | Prevents false Mild→Moderate |
| 2 | SpO₂ cross-contamination | Over-counting | Flags uncertain coupling |
| 3 | Cheyne-Stokes trough scoring | Over-counting | Prevents HF misdiagnosis as OSA |
| 4 | Low-confidence defaults | Over-counting | Confidence stratification |
| 5 | Artefact-flank exclusion | Over-counting | Prevents post-disconnect events |
| 6 | Local baseline validation | Over-counting | Rejects inflated-baseline FPs |
| 7 | Peak-based amplitude detection | Under-counting | AASM-conformant breath-level |
| 8 | Extended SpO₂ nadir window | Under-counting | Catches delayed desaturations |
| 9 | Flow smoothing removal | Under-counting | Eliminated +54 FPs on PSG-IPA |
| 10 | Position signal auto-mapping | Under-counting | Handles raw ADC encoding |
| 11 | Configurable profiles | Under-counting | Sensitivity adjustment per study |
| 12 | Flattening-based RERA | Under-counting | Flow limitation without amplitude drop |
Architecture
~8,900 lines across 17 submodules, 115 unit tests (CI: Python 3.9–3.12):
constants · utils · signal · breath · classify · spo2 · plm · ancillary · respiratory · pipeline · ml_classifier · profiles · postprocess · signal_quality · signal_quality_channels · ecg_effort · _types
Related
- YASAFlaskified — web platform integrating psgscoring with YASA staging, multilingual PDF reports, EDF+ export, and FHIR R4
- YASA — AI-based sleep staging (Vallat & Walker, eLife 2021)
- slaapkliniek.be — live instance (no installation required)
Citation
@article{rombaut2026psgscoring,
title = {Automated Polysomnography Scoring for Clinical Sleep Medicine:
An Open-Source Platform Validated Against 59 Independent
Scorer Sessions on {PSG-IPA}},
author = {Rombaut, Bart and Rombaut, Briek and Rombaut, Cedric},
year = {2026},
note = {Manuscript in preparation}
}
Disclaimer
psgscoring is research software — not a medical device. It is not CE-marked (MDR 2017/745) or FDA-cleared. All outputs are research-grade estimates that must be reviewed by a qualified clinician before any diagnostic or therapeutic decision. See DISCLAIMER.md for the full text.
License
BSD-3-Clause. See LICENSE.
Contact: bart.rombaut@gmail.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file psgscoring-0.7.1.tar.gz.
File metadata
- Download URL: psgscoring-0.7.1.tar.gz
- Upload date:
- Size: 464.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21d5c6ee86d45f959857fc464bcd963db2fb07e28d2ffc331de0f75d839614b5
|
|
| MD5 |
ec76c1525edf0b3cef44d1ba70337dd1
|
|
| BLAKE2b-256 |
d718f5c57153714674bea9700dd7466aa36b4f381b099fc35cb170b48da2b769
|
Provenance
The following attestation bundles were made for psgscoring-0.7.1.tar.gz:
Publisher:
publish.yml on bartromb/psgscoring
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
psgscoring-0.7.1.tar.gz -
Subject digest:
21d5c6ee86d45f959857fc464bcd963db2fb07e28d2ffc331de0f75d839614b5 - Sigstore transparency entry: 1710457197
- Sigstore integration time:
-
Permalink:
bartromb/psgscoring@9a847d624f7dd757fbbc1cc49dff2671589b6e61 -
Branch / Tag:
refs/tags/v0.7.1 - Owner: https://github.com/bartromb
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9a847d624f7dd757fbbc1cc49dff2671589b6e61 -
Trigger Event:
release
-
Statement type:
File details
Details for the file psgscoring-0.7.1-py3-none-any.whl.
File metadata
- Download URL: psgscoring-0.7.1-py3-none-any.whl
- Upload date:
- Size: 450.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7152322296bd9bb668eebe8fed4c980bb860bb52b9dfac24531e5447e24f021
|
|
| MD5 |
bd8cd079de18168832f2be2a5055a02b
|
|
| BLAKE2b-256 |
e846af31e11f57e50d2a250242447afa4979ff63367afe30e4ccf0fc5ead5ccb
|
Provenance
The following attestation bundles were made for psgscoring-0.7.1-py3-none-any.whl:
Publisher:
publish.yml on bartromb/psgscoring
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
psgscoring-0.7.1-py3-none-any.whl -
Subject digest:
c7152322296bd9bb668eebe8fed4c980bb860bb52b9dfac24531e5447e24f021 - Sigstore transparency entry: 1710457220
- Sigstore integration time:
-
Permalink:
bartromb/psgscoring@9a847d624f7dd757fbbc1cc49dff2671589b6e61 -
Branch / Tag:
refs/tags/v0.7.1 - Owner: https://github.com/bartromb
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9a847d624f7dd757fbbc1cc49dff2671589b6e61 -
Trigger Event:
release
-
Statement type: