Skip to content

Commit

Permalink
update coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Koaha committed Nov 27, 2024
1 parent c2a0a92 commit 573dbee
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 106 deletions.
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[run]
omit =
vital_sqi/app/*
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
DISPLAY: :99.0
run: |
Xvfb :99 & sleep 3
pytest --cov=vital_sqi --cov-report=xml --cov-report=term-missing tests
pytest --cov=vital_sqi --cov-config=.coveragerc --cov-report=xml --cov-report=term-missing tests
- name: Debug coverage files
run: |
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ doc-style: ## convert documentation style to numpy style
pyment -o numpydoc -w $(filename)

test: ## run tests with coverage report
pytest --cov=vital_sqi --cov-report term tests/
pytest --cov=vital_sqi --cov-config=.coveragerc --cov-report term tests/

BROWSER ?= firefox

cov: ## Run tests and show coverage report by file in the terminal
pytest --cov=vital_sqi --browser=$(BROWSER) tests/
pytest --cov=vital_sqi --cov-config=.coveragerc --browser=$(BROWSER) tests/
coverage report -m

test-all: ## run tests on every Python version with tox
Expand Down
248 changes: 154 additions & 94 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,125 +9,160 @@
![GitHub stars](https://img.shields.io/github/stars/Oucru-Innovations/vital-sqi?style=social)
![Build Status](https://github.com/Oucru-Innovations/vital-sqi/actions/workflows/ci.yml/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/Oucru-Innovations/vital-sqi/badge.svg?branch=main)](https://coveralls.io/github/Oucru-Innovations/vital-sqi?branch=main)
<!-- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -->
![GitHub License](https://img.shields.io/github/license/Oucru-Innovations/vital-sqi)
![Python Versions](https://img.shields.io/badge/python-3.7%2B-blue)
[![Documentation Status](https://readthedocs.org/projects/vital-sqi/badge/?version=latest)](https://vital-sqi.readthedocs.io/en/latest/?badge=latest)
<!-- ![PyPI Downloads](https://img.shields.io/pypi/dm/vitalsqi)
[![PyPI version](https://badge.fury.io/py/vitalsqi.svg)](https://badge.fury.io/py/vitalsqi)
[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Oucru-Innovations/vital-sqi/blob/main/docs/source/notebooks/synthesize_data.ipynb) -->

# Description
Vital_sqi is a Python package for signal quality index (SQI) extraction and quality assignment
for ECG and PPG waveforms. The package provides:
---

1. Support PPG and ECG waveforms in various data formats extracted from wearables.
2. Unified point of access for the current state-of-the-art SQIs: standard statistics, HRV, RR interval, and waveform based SQIs.
3. Rule-based classification of signal quality using user-defined thresholds for the extracted SQIs per signal segment.
4. Pipeline and GUIs for SQI extraction and quality assignment.
# Overview

**Vital_sqi** is an open-source Python package for **signal quality index (SQI)** extraction and **quality classification** of ECG and PPG signals. The package enables streamlined signal quality control, essential for reliable health monitoring and clinical research.

## Key Features:
1. Support for **PPG and ECG signals** in various formats, including wearable device data.
2. Implementation of **state-of-the-art SQI extraction methods** (e.g., statistical, HRV, waveform-based).
3. Rule-based **quality classification** using user-defined thresholds.
4. **Pipelines** for end-to-end processing, from data ingestion to SQI classification.
5. A **GUI tool** for defining rules and visualizing results interactively.

---

# Prerequisites and Installation

The package works with `Python 3.7` and `Python 3.8`.
The package supports Python `3.7` and `3.8`. Install Vital_sqi using pip:

```cmd
```bash
pip install vital-sqi
```

# Getting started
The package is built around three classes: `SignalSQI` `Rule` `Ruleset`

1. `signal_obj` `SignalSQI` object, has the following attributes:
- `signal_obj.signal` containing waveform data (pandas dataframe).
- `signal_obj.sampling_rate` containing sampling rate, either input by user or automatically inferred from the waveform.
- `signal_obj.sqis` containing SQIs (pandas dataframe) that are derived by functions of `vital_sqi.sqi` modules or an
external table, with SQI values as columns and signal segments as rows. After signal classification, decision of
`accept` or `reject` for each signal segment is in `decision` column. If signal segmentation is done with the package,
the table will contain also coordinates in column `start` and `end`.
- `signal_obj.rules` and `signal_obj.ruleset` containing a list of `rules` and a `ruleset` used for signal classification.
2. `rules` list of `Rule` objects, in which each corresponds to an SQI and contains thresholds for quality assignment.
Rules could be read into `signal_obj` from `.json` file in the following format:
```python
"test_sqi": {
"name": "test_sqi",
"def": [
{"op": ">", "value": "10", "label": "reject"},
{"op": ">=", "value": "3", "label": "accept"},
{"op": "<", "value": "3", "label": "reject"}],
"desc": "",
"ref": ""
### Optional Dependencies:
- **Dash** for GUI applications.
- **Plotly** for visualization.
- **Matplotlib** for advanced plotting options.

For detailed dependencies, refer to the [documentation](https://vital-sqi.readthedocs.io/en/latest/).

---

# Getting Started

The core of the package is built around three main classes:

### **1. SignalSQI Class**
This class handles the main signal data and SQI extraction workflow.
Attributes:
- `signal`: Waveform data (as a pandas DataFrame).
- `sampling_rate`: Sampling rate of the signal (user-defined or auto-inferred).
- `sqis`: SQI values calculated for signal segments.
- `rules` and `ruleset`: Lists of applied rules and the ruleset used for classification.

### **2. Rule Class**
Defines individual SQI-based rules for classification.
Example rule structure in JSON:
```json
{
"test_sqi": {
"name": "test_sqi",
"def": [
{"op": ">", "value": "10", "label": "reject"},
{"op": ">=", "value": "3", "label": "accept"},
{"op": "<", "value": "3", "label": "reject"}
],
"desc": "",
"ref": ""
}
```
3. `ruleset` object of class `Ruleset` contains a set of selected `rules` (selected from the list of rules in `signal_obj.rule`) and the order to apply them in quality
}
```

### **3. Ruleset Class**
Groups rules and defines the sequence for applying them to signal segments. The ruleset is defined in JSON format and can be customized to fit specific needs.`ruleset` object of class `Ruleset` contains a set of selected `rules` (selected from the list of rules in `signal_obj.rule`) and the order to apply them in quality
assignment (see schema below). Notice that this is not a tree-based classification.

![Example of a rule set](images/resize_sample_rule_chart.png "Example of a rule set")

## Pipelines
The package includes two pipelines for ECG (similarly for PPG) data as follows:
- `vital_sqi.pipeline_highlevel.get_ecg_sqis` to extract SQIs for ECG segments.
```python
from vital_sqi.pipeline.pipeline_highlevel import *
from vital_sqi.data.signal_sqi_class import SignalSQI
import os
file_in = os.path.abspath('tests/test_data/example.edf')
sqi_dict = os.path.abspath('tests/test_data/sqi_dict.json')
segments, signal_sqi_obj = get_ecg_sqis(file_in, sqi_dict, 'edf')
```

- `vital_sqi.pipeline_highlevel.get_qualified_ecg` to extract SQIs, use those to classify ECG signal as `accept` or
`reject` using user-defined thresholds. The `rules` and `ruleset` are defined in json format. Templates are found in
`vital_sqi/resource` folder: `sqi_dict.json` for `rules` and `rule_dict_test.json` for `ruleset`.
```python
from vital_sqi.pipeline.pipeline_highlevel import *
from vital_sqi.data.signal_sqi_class import SignalSQI
import os
file_in = os.path.abspath('tests/test_data/example.edf')
sqi_dict = os.path.abspath('tests/test_data/sqi_dict.json')
rule_dict_filename = os.path.abspath('tests/test_data/rule_dict_test.json')
ruleset_order = {3: 'skewness_1', 2: 'entropy', 1: 'perfusion'}
output_dir = tempfile.gettempdir()
signal_obj = get_qualified_ecg(file_name=file_in,
sqi_dict_filename=sqi_dict,
file_type='edf', duration=30,
rule_dict_filename=rule_dict_filename,
ruleset_order=ruleset_order,
output_dir=output_dir)
```

We also provide an GUI to easily define `rule` and `ruleset`, and execute them with an input SQI table (Help - hyperlink to readthedocs)

## Main steps
Following are the main steps to use the package for SQI extraction and signal classification. For details, see the
[documentation](https://vitalsqi.readthedocs.io/en/latest/).

**1. Reading/Writing**

Signal waveform is read into an object of `SignalSQI` class and written to the following format using `vital_sqi.data`
module.
- ECG: EDF, MIT (physio.net), csv.
- PPG: csv.
Classified segments are written to csv files using `vital_sqi.preprocess.segment_split.save_segment`

**2.Preprocessing and segmentation**
`vital_sqi.preprocessing` allows doing:
- Several signal preprocessing steps such as trimming, tapering, smoothing, bandpass filter etc. For PPG,
it is possible to filter the signal based on columns such as SpO2, perfusion, etc.
- Signal segmentation has two options: splitting by duration and by beat (which includes beat detection).

**3. SQI extraction**

The implemented SQIs, `vital_sqi.sqi` module, are divided into 4 groups:
- Standard statistics SQIs such as kurtosis, skewness, entropy, etc.
- Heart rate variability (HRV) based SQIs such as sdnn, sdsd, rmssd, etc.
- RR interval-based SQIs such as ectopic, correlogram, msq, etc.
- Waveform-based SQIs: dtw, qrs_energy, qrs_a etc.
# Pipelines

The package includes predefined pipelines for processing ECG and PPG signals.

### Example: Extracting SQIs from ECG
```python
from vital_sqi.pipeline.pipeline_highlevel import *
from vital_sqi.data.signal_sqi_class import SignalSQI
import os

file_in = os.path.abspath('tests/test_data/example.edf')
sqi_dict = os.path.abspath('tests/test_data/sqi_dict.json')
segments, signal_sqi_obj = get_ecg_sqis(file_in, sqi_dict, 'edf')
```

### Example: Quality Classification for ECG
```python
from vital_sqi.pipeline.pipeline_highlevel import *
import os
import tempfile

file_in = os.path.abspath('tests/test_data/example.edf')
sqi_dict = os.path.abspath('tests/test_data/sqi_dict.json')
rule_dict_filename = os.path.abspath('tests/test_data/rule_dict_test.json')
ruleset_order = {3: 'skewness_1', 2: 'entropy', 1: 'perfusion'}
output_dir = tempfile.gettempdir()

signal_obj = get_qualified_ecg(
file_name=file_in,
sqi_dict_filename=sqi_dict,
file_type='edf',
duration=30,
rule_dict_filename=rule_dict_filename,
ruleset_order=ruleset_order,
output_dir=output_dir
)
```

---

# GUI for Rules and Execution

Vital_sqi provides a GUI for creating rules, defining rulesets, and executing them interactively. The GUI helps users:
- Configure rules visually.
- Test and validate signal quality thresholds.
- Export results for further analysis.

[Click here for the GUI guide](https://vital-sqi.readthedocs.io/en/latest/docstring/vital_sqi.app.html#module-vital_sqi.app.app).


# Workflow Overview

### **1. Reading and Writing Signals**
Supported formats:
- ECG: `EDF`, `MIT`, `CSV`.
- PPG: `CSV`.

### **2. Preprocessing and Segmentation**
Available preprocessing steps:
- Trimming, tapering, and smoothing.
- Bandpass filtering.
- Segmentation by duration or by beat.

### **3. SQI Extraction**
Four types of SQIs:
1. **Statistical SQIs**: Kurtosis, skewness, entropy, etc.
2. **HRV-based SQIs**: SDNN, SDSD, RMSSD, etc.
3. **RR Interval-based SQIs**: Ectopic, correlogram, etc.
4. **Waveform-based SQIs**: DTW, QRS energy, etc.

The function `vital_sqi.pipeline_function.extract_sqi` is used to extract a number of SQIs from segments. The requested SQIs
are defined in a json file called SQI dictionary. We provide a dictionary template for all implemented SQIs, with default
parameters, in `vital_sqi/resource/sqi_dict.json`.

**4. Quality classification**
### **4. Signal Quality Classification**
- Rule-based classification using user-defined thresholds.
- Optimized rule application for performance.

Templates for rules and rulesets are available in the `vital_sqi/resource` directory.

The package allows making rule set from SQIs and user-defined thresholds for quality classification. A segment assigned
as `accept` pass all rules in the set, otherwise `reject`. Rules in the set have ordered, which might help to
Expand All @@ -136,7 +171,32 @@ improve speed.
We ran brute force threshold searching for an in-house PPG dataset (generated with Smartcare, doubly annotated
by domain experts) to obtain a set of recommended thresholds, as found in `resource/rule_dict.json`.

---

# Documentation

Find detailed tutorials, examples, and API references at:
🔗 [Vital_sqi Documentation](https://vital-sqi.readthedocs.io/en/latest/)

---

# Contributions

We welcome contributions from the community! Please refer to our [CONTRIBUTING.md](https://github.com/Oucru-Innovations/vital-sqi/blob/main/CONTRIBUTING.md) for guidelines.

---

# References

- [Optimal Signal Quality Index for Photoplethysmogram Signals](https://doi.org/10.xxxx)
- [Other relevant papers and research articles]

---

# License

Vital_sqi is licensed under the MIT License. See the [LICENSE](https://github.com/Oucru-Innovations/vital-sqi/blob/main/LICENSE) file for details.

---

Thank you for supporting Vital_sqi! For questions or issues, feel free to [open an issue](https://github.com/Oucru-Innovations/vital-sqi/issues) on GitHub.
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
addopts = --ignore=vital_sqi/app
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
vitalDSP
openpyxl
astropy>=4.0.0
matplotlib
numpy>=1.20.2
pandas>=1.1.5
Expand Down
15 changes: 8 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,22 @@
long_description = f.read()

setup(
name = 'vital_sqi',
version = '0.1.0',
name = 'vitalSQI',
version = '0.1.1',
packages = find_packages(include = ["vital_sqi", "vital_sqi.*"]),
description = "Signal quality control pipeline for electrocardiogram and "
"photoplethysmogram",
"photoplethysmogram",
long_description = long_description,
long_description_content_type = 'text/markdown',
author = 'Khoa Le, Hai Ho, Stefan Karolcik, Heloise Greeff',
author_email = '[email protected], [email protected], [email protected], '
'[email protected]',
'[email protected]',
maintainer = 'Hai Ho, Khoa Le',
maintainer_email = '[email protected], [email protected]',
py_modules = ['common', 'data', 'preprocess', 'sqi'],
install_requires = [
'vitalDSP',
'openpyxl',
'astropy>=4.0.0',
'pmdarima>=1.8.0',
'matplotlib',
'numpy>=1.20.2',
Expand All @@ -44,13 +43,15 @@
"vital_sqi": ["resource/*.json"],
},
zip_safe = False,
url = 'https://github.com/meta00/vital_sqi',
url = 'https://github.com/Oucru-Innovations/vital-sqi',
license = 'MIT',
classifiers = [
'Intended Audience :: Developers',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8'
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.11'
],
)
Loading

0 comments on commit 573dbee

Please sign in to comment.