Metadata-Version: 2.4
Name: cardiacprep
Version: 1.0.0
Summary: ECG and accelerometry (EDF) preprocessing pipeline for multi-day wearable recordings
Author: Anna Bator, Stefan van Duijvenboden
Maintainer: Anna Bator
License: ACADEMIC USE LICENCE
        ====================
        
        These licence terms apply to all licences granted by THE CHANCELLOR, MASTERS
        AND SCHOLARS OF THE UNIVERSITY OF OXFORD whose administrative offices are at
        University Offices, Wellington Square, Oxford OX1 2JD, United Kingdom (the
        "University") for use of "cardiac-prep" ("the Software") through this website
        (https://github.com/OxWearables/cardiac-prep) (the "Website").
        
        By downloading the Software through the Website, you (the "Licensee") are
        confirming that you agree that your use of the Software is subject to these
        licence terms.
        
        PLEASE READ THESE LICENCE TERMS CAREFULLY BEFORE DOWNLOADING THE SOFTWARE. IF
        YOU DO NOT AGREE TO THESE LICENCE TERMS YOU SHOULD NOT DOWNLOAD THE SOFTWARE.
        
        THE SOFTWARE IS INTENDED FOR USE BY ACADEMICS CARRYING OUT RESEARCH AND NOT
        FOR USE BY CONSUMERS OR COMMERCIAL BUSINESSES.
        
        
        1. ACADEMIC USE LICENCE
        -----------------------
        
        1.1  The Licensee is granted a limited non-exclusive and non-transferable
             royalty free licence to download and use the Software provided that the
             Licensee will:
        
             (a)  limit their use of the Software to their own internal academic
                  non-commercial research which is undertaken for the purposes of
                  education or other scholarly use;
        
             (b)  not use the Software for or on behalf of any third party or to
                  provide a service or integrate all or part of the Software into a
                  product for sale or license to third parties;
        
             (c)  use the Software in accordance with the prevailing instructions and
                  guidance for use given on the Website and comply with procedures on
                  the Website for user identification, authentication and access;
        
             (d)  comply with all applicable laws and regulations with respect to
                  their use of the Software; and
        
             (e)  ensure that the Copyright Notice "Copyright © 2026, University of
                  Oxford" appears prominently wherever the Software is reproduced and
                  is referenced or cited with the Copyright Notice when the Software
                  is described in any research publication or on any documents or
                  other material created using the Software.
        
        1.2  The Licensee may only reproduce, modify, transmit or transfer the
             Software where:
        
             (a)  such reproduction, modification, transmission or transfer is for
                  academic, research or other scholarly use;
        
             (b)  the conditions of this Licence are imposed upon the receiver of the
                  Software or any modified Software;
        
             (c)  all original and modified Source Code is included in any transmitted
                  software program; and
        
             (d)  the Licensee grants the University an irrevocable, indefinite,
                  royalty free, non-exclusive unlimited licence to use and sub-licence
                  any modified Source Code as part of the Software.
        
        1.3  The University reserves the right at any time and without liability or
             prior notice to the Licensee to revise, modify and replace the
             functionality and performance of the access to and operation of the
             Software.
        
        1.4  The Licensee acknowledges and agrees that the University owns all
             intellectual property rights in the Software. The Licensee shall not have
             any right, title or interest in the Software.
        
        1.5  This Licence will terminate immediately and the Licensee will no longer
             have any right to use the Software or exercise any of the rights granted
             to the Licensee upon any breach of the conditions in Section 1 of this
             Licence.
        
        
        2. INDEMNITY AND LIABILITY
        --------------------------
        
        2.1  The Licensee shall defend, indemnify and hold harmless the University
             against any claims, actions, proceedings, losses, damages, expenses and
             costs (including without limitation court costs and reasonable legal
             fees) arising out of or in connection with the Licensee's possession or
             use of the Software, or any breach of these terms by the Licensee.
        
        2.2  The Software is provided on an 'as is' basis and the Licensee uses the
             Software at their own risk. No representations, conditions, warranties or
             other terms of any kind are given in respect of the Software and all
             statutory warranties and conditions are excluded to the fullest extent
             permitted by law. Without affecting the generality of the previous
             sentences, the University gives no implied or express warranty and makes
             no representation that the Software or any part of the Software: (a) will
             enable specific results to be obtained; or (b) meets a particular
             specification or is comprehensive within its field or that it is error
             free or will operate without interruption; or (c) is suitable for any
             particular, or the Licensee's specific purposes.
        
        2.3  Except in relation to fraud, death or personal injury, the University's
             liability to the Licensee for any use of the Software, in negligence or
             arising in any other way out of the subject matter of these licence
             terms, will not extend to any incidental or consequential damages or
             losses, or any loss of profits, loss of revenue, loss of data, loss of
             contracts or opportunity, whether direct or indirect.
        
        2.4  The Licensee hereby irrevocably undertakes to the University not to make
             any claim against any employee, student, researcher or other individual
             engaged by the University, being a claim which seeks to enforce against
             any of them any liability whatsoever in connection with these licence
             terms or their subject-matter.
        
        
        3. GENERAL
        ----------
        
        3.1  Severability - If any provision (or part of a provision) of these licence
             terms is found by any court or administrative body of competent
             jurisdiction to be invalid, unenforceable or illegal, the other
             provisions shall remain in force.
        
        3.2  Entire Agreement - These licence terms constitute the whole agreement
             between the parties and supersede any previous arrangement, understanding
             or agreement between them relating to the Software.
        
        3.3  Law and Jurisdiction - These licence terms and any disputes or claims
             arising out of or in connection with them shall be governed by, and
             construed in accordance with, the law of England. The Licensee
             irrevocably submits to the exclusive jurisdiction of the English courts
             for any dispute or claim that arises out of or in connection with these
             licence terms.
        
        
        If you are interested in using the Software commercially, please contact
        Oxford University Innovation Limited to negotiate a licence. Contact details
        are enquiries@innovation.ox.ac.uk
        
Project-URL: Homepage, https://github.com/OxWearables/cardiac-prep
Project-URL: Repository, https://github.com/OxWearables/cardiac-prep
Keywords: ecg,hrv,accelerometry,wearables,edf,physical-activity
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: <3.13,>=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: actipy>=3.8
Requires-Dist: matplotlib>=3.7
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: pyEDFlib>=0.1.40
Requires-Dist: PyYAML>=6.0
Requires-Dist: reportlab>=4.0
Requires-Dist: scikit-learn>=1.3
Requires-Dist: scipy>=1.10
Requires-Dist: seaborn>=0.13
Requires-Dist: tensorflow>=2.16
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Dynamic: license-file

# ECG & Accelerometry (EDF) Preprocessing Pipeline

[![CI](https://github.com/OxWearables/cardiac-prep/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/OxWearables/cardiac-prep/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/readthedocs/cardiac-prep)](https://cardiac-prep.readthedocs.io/)
[![Python 3.9–3.12](https://img.shields.io/badge/python-3.9%20%E2%80%93%203.12-blue)](https://www.python.org/downloads/)
[![Licence: Academic Use](https://img.shields.io/badge/licence-academic%20use-green)](LICENSE)

An end-to-end Python pipeline for processing multi-day wearable sensor data from `.EDF` files. It extracts heart rate, heart rate variability (HRV) and physical activity metrics, performs quality control and imputation, and generates summary reports and visualisations.

📖 **Full documentation: [cardiac-prep.readthedocs.io](https://cardiac-prep.readthedocs.io/)**

You do not need to be a programmer to use this. Follow the steps in order, copying each command exactly.

---

## 🚦 The three commands

Once set up, this is the whole pipeline. Run them in this order:

### 1. Process your recordings

```
cardiac-prep process
```

Reads every `.edf` file in `input_data` and writes results to `output`. About 30 seconds per file.

### 2. Summarise the whole dataset

```
cardiac-prep summarise
```

Creates population-level plots across all participants.

### 3. Inspect one participant (optional)

```
cardiac-prep inspect --list
```

```
cardiac-prep inspect --subject NAME_FROM_THE_LIST
```

Add `--show` to open the plots in a window as well as saving them.

> **First time here?** Do the [Setup](#setup-) below first. Add `--help` to any command to see its options.

---

## Features ✨
* **Parallel Processing**: Uses all **CPU** cores except one, so your machine stays usable (no GPU needed). Configurable via `n_processes`.
* **Preprocessing**: QRS detection, artifact removal, signal quality assessment.
* **Data Imputation**: Linear interpolation for short gaps, time-of-day averaging for longer ones.
* **Comprehensive Metrics**:
    * Heart Rate (Resting, Min, Max, Average)
    * Heart Rate Variability (RMSSD and log-normalised RMSSD to account for HR)
    * Physical Activity (time in Sedentary, Light, Moderate and Vigorous zones)
* **Automated Reporting**: A 2-page PDF summary report per participant.
* **Dataset Summarisation**: Population-level trend plots.

---

## Before you start 📝

You need **Python 3.9, 3.10, 3.11 or 3.12**. Python 3.13 is not yet supported.

To check what you have, open a terminal and type:

```
python --version
```

If that says "command not found", or shows a version outside the range, install **Miniforge** from <https://conda-forge.org/download/> and reopen your terminal.

> **Opening a terminal:** on **macOS** press `Cmd + Space`, type "Terminal", press Enter. On **Windows** press Start, type "Miniforge Prompt", press Enter. On **Linux** press `Ctrl + Alt + T`.

---

## Setup 🚀

You only do this once.

### Step 1 – Create a separate environment

An environment is a private space for this project's software, so it cannot clash with anything else on your computer. **Pick ONE option.**

**Option A – conda (recommended)**

```
conda create -n cardiacprep python=3.11 -y
```

```
conda activate cardiacprep
```

**Option B – venv (built into Python)**

```
python -m venv .venv
```

Activate it. On **macOS or Linux**:

```
source .venv/bin/activate
```

On **Windows**:

```
.venv\Scripts\activate
```

✅ **How to tell it worked:** your prompt now starts with `(cardiacprep)` or `(.venv)`.

> ⚠️ Activate the environment **every time** you open a new terminal. If your prompt does not show the name in brackets, run the activate line again.

### Step 2 – Install the pipeline

```
pip install cardiacprep
```

This takes several minutes. It is finished when your prompt reappears.

### Step 3 – Set up a folder for your study

Go to wherever you want your results to live, then:

```
cardiac-prep init
```

This creates `config.yaml` plus the `input_data`, `output` and `models` folders, and prints the handful of settings worth checking before you start.

### Step 4 – The heartbeat detector

The pipeline uses a machine-learning model to find heartbeats. It is too big for this repository, so it is downloaded separately.

**You do not need to do anything here.** The first run fetches it into the `models` folder, checks it against a known checksum, and carries on.

To fetch it now instead, useful before working somewhere with no wifi:

```
cardiac-prep download
```

> ⚠️ No internet on the machine doing the processing? Fetch the model elsewhere, copy the `.keras` file into `models`, and set `auto_download_model: false` in `config.yaml`.

### Step 5 – Check it works

```
cardiac-prep --help
```

If you see a list of options, setup is complete. 🎉

---

## Running it 🏃

Put your `.edf` files into the `input_data` folder, then use [the three commands](#-the-three-commands) above.

To preview what will be processed without processing anything:

```
cardiac-prep process --dry-run
```

To use folders elsewhere on your computer:

```
cardiac-prep process --input /path/to/my/edfs --output /path/to/my/results
```

---

## Changing settings ⚙️

All settings live in **`config.yaml`**. Open it in any text editor. Every setting is explained in comments and every one is optional, so deleting a line restores its default.

You should **never need to edit a `.py` file**.

| Setting | What it does |
|---|---|
| `input_dir`, `output_dir` | Where recordings are read from and results written to |
| `n_processes` | How many recordings to process at once. Set to `1` for clearer error messages |
| `night_start_hour`, `night_end_hour` | The hours treated as overnight rest (see below) |
| `activity_*_mg` | Movement cut-points separating activity intensity levels |

Mistakes are reported clearly and stop the run immediately, before anything is processed.

---

## Outputs 📂

An `./output/` directory containing:

* **Participant folders**: a `plots` directory with the PDF report, and a `processed_data` directory with detailed CSVs.
* **Top-level files**: an aggregated `df_info_summary.csv.gz` plus dataset-wide summary plots.

Every row of `df_info_summary.csv.gz` records `model_file` and `model_sha256`, identifying exactly which detector produced those results. Failed recordings get `failed = 1` and a `failure_reason` explaining why.

### Reading `*_df_qc.csv.gz`

**One row = one 10-second segment** of the recording. The index is seconds from the start, so row `0` is 00:00:00–00:00:10, row `10` is the next segment, and so on.

| Column group | Meaning |
|---|---|
| `device_worn`, `clipped_5perc_thrs`, `passed_initialQC` | Per-segment screening: was the device worn, was the signal saturated, did it pass basic signal checks |
| `passed_finalQC` | Whether the segment's heartbeats were good enough to trust |
| `N_beats`, `N_RR`, `rr_Cover`, `rr_sd`, `rr_outliers`, `qrs_snr`, `qrs_amp`, `rmssd` | Beat-level measurements |
| `*_raw` | The measured value, blank wherever QC failed |
| `*_imputed` | The gap-filled value. **Use these for analysis** |
| `*_isImputed` | `True` where that value was filled rather than measured |

**Why are the beat columns so often blank?** They are only computed for segments that were actually analysed. A segment is skipped when the device was not worn, the signal was too noisy or clipped to pass initial checks, or the detector found fewer than `n_beats_min` beats. Separately, `RRm_raw` is deliberately blanked wherever `passed_finalQC` is `False`, so blank means "not measurable here", not "missing data".

Lots of blanks therefore means a lot of non-wear time or poor signal quality, and is expected rather than a bug. To judge quality fairly, look at `prop_ECG_worn_passed_finalQC` in the summary file: it measures quality **among worn time only**, so it is not diluted by periods when the device was off.

---

## Things to be aware of ⚠️

**Resting heart rate and HRV use a fixed 21:00–09:00 window.** Participants who sleep outside those hours, such as shift workers or someone who regularly sleeps 02:00–11:00, will have these metrics computed from the wrong hours or not at all. You can move the window in `config.yaml`, but it is one fixed window applied to everybody.

*Planned improvement:* detect each participant's main sleep period from their own accelerometer data instead of assuming a clock window.

**Activity cut-points come from older adults.** The thresholds are from Etzkorn et al. (2024), derived in 381 adults with a median age of 78. Interpret cautiously in younger populations.

---

## A note on HRV normalisation

This pipeline uses the natural logarithm of RMSSD to normalise HRV. This removes the mathematical influence of a person's average heart rate, allowing fairer comparisons. For a detailed explanation, see [Should we normalize HRV by heart rate?](https://marcoaltini.substack.com/p/should-we-normalize-hrv-by-heart) by Marco Altini.

---

## If something goes wrong 🔧

| What you see | What to do |
|---|---|
| `command not found: conda` | Install Miniforge, then close and reopen your terminal |
| `No module named ...` | Your environment is not active. Run `conda activate cardiacprep` |
| `No '*.keras' file found` | The detector model is missing. Redo **Setup Step 4** |
| `Found 2 model files` | Remove the spare `.keras` file from `models` |
| `No .edf files found` | Your recordings are not in `input_data`, or do not end in `.edf` |
| `Configuration problem: ...` | A mistake in `config.yaml`. The message names the setting |
| One file failed | The others still process. Rerun with `--jobs 1` to see the error clearly |

---

## For developers 🛠️

Work from a clone rather than the released package:

```
git clone https://github.com/OxWearables/cardiac-prep.git
```

```
cd cardiac-prep && pip install -e ".[dev]"
```

```
pytest
```

Tests use synthetic data only, so no recordings or model weights are needed. GitHub Actions runs the tests and `ruff check .` on Python 3.9–3.12 for every push.

A clone also has `process.py`, `summarise_dataset.py` and `plot_subject.py` at the root, which run the same three subcommands without installing anything.

See the [developer documentation](https://cardiac-prep.readthedocs.io/en/latest/development.html) for the layout, how the docs are built, and how releases are made.

---

## Licence

Academic Use Licence, see [LICENSE](LICENSE). Free for academic, non-commercial
research and teaching. Commercial use requires a separate licence - contact
Oxford University Innovation at enquiries@innovation.ox.ac.uk.

Copyright © 2026, University of Oxford.

## Authors
* **Stefan van Duijvenboden**
* **Anna Bator**
