Skip to content

Bsmit3021/SleepSignalOps

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SleepSignalOps: Sleep Apnea Detection from ECG Signals

A production-ready machine learning pipeline for detecting sleep apnea from ECG signals using real clinical data from PhysioNet's Apnea-ECG Database.

Author: Brian Smith
Year: 2026

Python 3.8+ License: MIT


🎯 Overview

This project implements a complete end-to-end system for sleep apnea detection, including:

  • Data ingestion from PhysioNet databases
  • Signal quality assessment and automated quality control
  • Feature extraction (HRV time/frequency domain, QRS morphology)
  • Machine learning models for apnea detection
  • Patient-level AHI estimation from minute-level predictions
  • Comprehensive evaluation framework

Key Features

βœ… Real Clinical Data: 70 overnight ECG recordings from PhysioNet
βœ… Automated Quality Control: Signal quality classifier with gating
βœ… Rich Feature Engineering: 36 HRV and QRS features per minute
βœ… Production-Ready: Modular architecture, model persistence, comprehensive testing
βœ… Transparent Evaluation: AUROC, sensitivity, specificity, calibration analysis


πŸ“Š Performance

Model Performance (Balanced Random Forest)

Metric Value
AUROC 0.755
Sensitivity 55.8%
Specificity 79.1%
Accuracy 72.7%
AHI Correlation +0.565
AHI MAE 11.81 events/hour

Example Predictions

Patient a16: True AHI=39.8 β†’ Predicted=39.3 (Error: -0.5) βœ…
Patient a18: True AHI=53.3 β†’ Predicted=54.2 (Error: +0.9) βœ…
Patient b03: True AHI=9.9  β†’ Predicted=6.5  (Error: -3.4) βœ…
Patient b05: True AHI=7.9  β†’ Predicted=9.1  (Error: +1.2) βœ…

πŸš€ Quick Start

Prerequisites

  • Python 3.8+
  • pip

Installation

  1. Install dependencies
pip install -r requirements.txt
  1. Download PhysioNet data
python scripts/download_physionet.py

This will download 70 ECG recordings (~500MB) to ../data/raw/.

Training the Model

Quick training (5 patients, ~2 minutes):

python scripts/train_apnea_model.py

Full training (22 patients, balanced, ~10 minutes):

python scripts/train_balanced_model.py

Model comparison (Logistic vs Random Forest):

python scripts/compare_models_no_xgb.py

Making Predictions

from models.baseline import ApneaClassifier
from features.signal_quality import SignalQualityMetrics

# Load trained model
model = ApneaClassifier.load('apnea_model_balanced.pkl')

# Check signal quality
qm = SignalQualityMetrics(fs=100)
quality = qm.comprehensive_quality_report(ecg_signal)
print(f"Signal Quality: {quality['quality_grade']} ({quality['overall_quality_score']:.1f}/100)")

# Make predictions (if quality is good)
if quality['overall_quality_score'] >= 60:
    predictions = model.predict_proba(features)
    ahi_estimate = (predictions >= 0.5).sum() / (len(predictions) / 60)
    print(f"Estimated AHI: {ahi_estimate:.1f} events/hour")

πŸ“ Project Structure

sleepsignalops/
β”œβ”€β”€ ingestion/              # Data loading modules
β”‚   β”œβ”€β”€ wfdb_loader.py      # Generic WFDB loader
β”‚   β”œβ”€β”€ physionet_apnea.py  # PhysioNet-specific loader
β”‚   └── csv_loader.py       # CSV time-series loader
β”œβ”€β”€ features/               # Feature extraction
β”‚   β”œβ”€β”€ signal_quality.py   # Quality metrics (SNR, flatline, etc.)
β”‚   β”œβ”€β”€ rr_intervals.py     # HRV features (time & frequency)
β”‚   β”œβ”€β”€ qrs_amplitude.py    # QRS morphology features
β”‚   └── demographic_features.py
β”œβ”€β”€ models/                 # ML models
β”‚   β”œβ”€β”€ baseline.py         # Logistic/RF/XGBoost classifiers
β”‚   └── quality_classifier.py
β”œβ”€β”€ evaluation/             # Evaluation metrics
β”‚   β”œβ”€β”€ metrics.py          # AUROC, sensitivity, specificity
β”‚   β”œβ”€β”€ calibration.py      # ECE, Brier score
β”‚   β”œβ”€β”€ subgroup.py         # Quality-stratified analysis
β”‚   └── patient_level.py    # AHI estimation
β”œβ”€β”€ serving/
β”‚   └── quality_gate.py     # Quality-gated inference
β”œβ”€β”€ scripts/                # Training & testing scripts
β”‚   β”œβ”€β”€ download_physionet.py
β”‚   β”œβ”€β”€ validate_pipeline.py
β”‚   β”œβ”€β”€ test_feature_extraction.py
β”‚   β”œβ”€β”€ test_quality_gate.py
β”‚   β”œβ”€β”€ train_apnea_model.py
β”‚   β”œβ”€β”€ train_balanced_model.py  # βœ… Best model
β”‚   └── compare_models_no_xgb.py
β”œβ”€β”€ tests/                  # Unit tests
β”‚   └── test_signal_quality.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ LICENSE
└── README.md

πŸ”¬ Methodology

Data Pipeline

  1. Data Ingestion: Load ECG signals and apnea annotations from PhysioNet
  2. Signal Quality Assessment: Compute SNR, flatline %, outliers, artifacts
  3. Feature Extraction: Extract HRV and QRS features in 60-second windows
  4. Quality Gating: Reject predictions on poor-quality signals
  5. Apnea Classification: Predict apnea minute-by-minute
  6. AHI Estimation: Aggregate to patient-level AHI

Features (36 per minute)

HRV Time-Domain (5 features):

  • SDNN, RMSSD, pNN50, mean HR, CV

HRV Frequency-Domain (5 features):

  • VLF, LF, HF power, total power, LF/HF ratio

QRS Amplitude (26 features):

  • R-peak amplitude (mean, std, min, max, CV)
  • QRS width, area, morphology variability

Models

  • Signal Quality Classifier: Random Forest (100% accuracy)
  • Apnea Detector: Random Forest (AUROC: 0.755)
  • Alternative: Logistic Regression, XGBoost (optional)

πŸ“– Usage Examples

1. Validate Pipeline

python scripts/validate_pipeline.py

2. Test Feature Extraction

python scripts/test_feature_extraction.py

3. Test Quality Gate System

python scripts/test_quality_gate.py

4. Load and Use Trained Model

from models.baseline import ApneaClassifier
import numpy as np

# Load model
model = ApneaClassifier.load('apnea_model_balanced.pkl')

# Your features (36 features per minute)
features = np.array([...])  # Shape: (n_minutes, 36)

# Predict
apnea_probabilities = model.predict_proba(features)
apnea_predictions = (apnea_probabilities >= 0.5).astype(int)

# Estimate AHI
duration_hours = len(features) / 60
ahi = apnea_predictions.sum() / duration_hours
print(f"Estimated AHI: {ahi:.1f} events/hour")

⚠️ Limitations

Known Limitations

  1. Feature Availability

    • Only ECG/HRV features available
    • Missing SpO2 (oxygen desaturation - strongest apnea indicator)
    • Missing respiratory effort signals
    • Missing sleep stage information
  2. Model Performance

    • AHI MAE: 11.81 events/hour (moderate error)
    • Severity agreement: 38.5% (room for improvement)
    • Some patients have large prediction errors
  3. Clinical Use

    • NOT approved for clinical use
    • Research and educational purposes only
    • Clinical systems use multi-modal signals (ECG + SpO2 + respiratory)

Why ECG-Only Detection is Challenging

Sleep apnea detection from ECG alone is inherently limited because:

  • Apnea is primarily a respiratory event (breathing cessation)
  • ECG captures cardiac response (secondary effect)
  • SpO2 desaturation is the gold standard indicator
  • Clinical systems use ECG + SpO2 + respiratory effort + sleep staging

This project demonstrates best practices for ECG-only detection but acknowledges these fundamental limitations.


πŸš€ Future Improvements

High Priority

  1. Add SpO2 Features (if data available)

    • Oxygen desaturation detection
    • Desaturation index calculation
    • Would significantly improve performance
  2. Episode-Level Detection

    • Detect apnea episodes (sequences) instead of minutes
    • More clinically meaningful
    • Better signal-to-noise ratio
  3. Deep Learning Models

    • CNN-BiLSTM for temporal patterns
    • Transformer with attention
    • Learn features automatically from raw signals

Medium Priority

  1. Cross-Validation

    • K-fold CV on all 70 records
    • More robust performance estimates
  2. Threshold Optimization

    • ROC curve analysis
    • Find optimal operating point
  3. Calibration Improvement

    • Platt scaling or isotonic regression

Low Priority

  1. API Deployment

    • FastAPI endpoints
    • Docker containerization
    • Streamlit dashboard
  2. Additional Datasets

    • Test on other PhysioNet databases
    • Cross-dataset validation

πŸ§ͺ Testing

Run unit tests:

pytest tests/

Run specific test:

pytest tests/test_signal_quality.py -v

πŸ“š References

Dataset

Key Papers

  • Penzel T, et al. "The Apnea-ECG Database" (2000)
  • Task Force of ESC/NASPE. "Heart rate variability" (1996)
  • Mendez MO, et al. "Sleep apnea screening by autoregressive models" (2007)

Tools

  • Python 3.8+
  • scikit-learn, pandas, numpy
  • WFDB Python package
  • PhysioNet databases

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ™ Acknowledgments

  • PhysioNet for providing the Apnea-ECG Database
  • WFDB team for the excellent Python package
  • scikit-learn community for ML tools

⭐ Citation

If you use this code in your research, please cite:

@software{sleepsignalops2026,
  title={SleepSignalOps: Sleep Apnea Detection from ECG Signals},
  author={Brian Smith},
  year={2026},
  url={https://github.com/B3smoove/SleepSignalOps}
}

Built with real data. Evaluated rigorously. Documented thoroughly. πŸŽ‰

About

Production-ready ML pipeline for sleep apnea detection from ECG signals (AUROC: 0.755). Features HRV/QRS extraction, signal quality gating, and patient-level AHI estimation on 70 PhysioNet patients.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages