atriakit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. atriakit-0.1.0/LICENSE +21 -0
  2. atriakit-0.1.0/PKG-INFO +330 -0
  3. atriakit-0.1.0/README.md +295 -0
  4. atriakit-0.1.0/atriakit/__init__.py +65 -0
  5. atriakit-0.1.0/atriakit/configs/annotations_loader_config.py +24 -0
  6. atriakit-0.1.0/atriakit/configs/feature_computation_config.py +45 -0
  7. atriakit-0.1.0/atriakit/configs/pipeline_config.py +32 -0
  8. atriakit-0.1.0/atriakit/configs/segment_config.py +11 -0
  9. atriakit-0.1.0/atriakit/configs/signal_preprocessor_config.py +48 -0
  10. atriakit-0.1.0/atriakit/constants.py +18 -0
  11. atriakit-0.1.0/atriakit/datasets.py +286 -0
  12. atriakit-0.1.0/atriakit/feature_calculator.py +1403 -0
  13. atriakit-0.1.0/atriakit/features/__init__.py +0 -0
  14. atriakit-0.1.0/atriakit/features/amplitude.py +19 -0
  15. atriakit-0.1.0/atriakit/features/area.py +104 -0
  16. atriakit-0.1.0/atriakit/features/complexity.py +51 -0
  17. atriakit-0.1.0/atriakit/features/entropy.py +51 -0
  18. atriakit-0.1.0/atriakit/features/fragments.py +117 -0
  19. atriakit-0.1.0/atriakit/features/morphology.py +189 -0
  20. atriakit-0.1.0/atriakit/features/vcg.py +81 -0
  21. atriakit-0.1.0/atriakit/io/__init__.py +13 -0
  22. atriakit-0.1.0/atriakit/io/annotations_loader.py +223 -0
  23. atriakit-0.1.0/atriakit/io/base_loader.py +15 -0
  24. atriakit-0.1.0/atriakit/io/dicom_loader.py +66 -0
  25. atriakit-0.1.0/atriakit/io/ecg_loader.py +47 -0
  26. atriakit-0.1.0/atriakit/models/__init__.py +3 -0
  27. atriakit-0.1.0/atriakit/models/annotation_row.py +18 -0
  28. atriakit-0.1.0/atriakit/models/annotation_schema.py +25 -0
  29. atriakit-0.1.0/atriakit/models/annotations.py +193 -0
  30. atriakit-0.1.0/atriakit/models/ecg_data.py +94 -0
  31. atriakit-0.1.0/atriakit/preprocessing/__init__.py +9 -0
  32. atriakit-0.1.0/atriakit/preprocessing/annotations.py +43 -0
  33. atriakit-0.1.0/atriakit/preprocessing/signals.py +247 -0
  34. atriakit-0.1.0/atriakit/processing/dataset.py +215 -0
  35. atriakit-0.1.0/atriakit/processing/pipeline.py +542 -0
  36. atriakit-0.1.0/atriakit/processing/segment_processor.py +122 -0
  37. atriakit-0.1.0/atriakit/utils.py +180 -0
  38. atriakit-0.1.0/atriakit.egg-info/PKG-INFO +330 -0
  39. atriakit-0.1.0/atriakit.egg-info/SOURCES.txt +53 -0
  40. atriakit-0.1.0/atriakit.egg-info/dependency_links.txt +1 -0
  41. atriakit-0.1.0/atriakit.egg-info/requires.txt +13 -0
  42. atriakit-0.1.0/atriakit.egg-info/top_level.txt +1 -0
  43. atriakit-0.1.0/pyproject.toml +72 -0
  44. atriakit-0.1.0/setup.cfg +4 -0
  45. atriakit-0.1.0/tests/test_annotations.py +328 -0
  46. atriakit-0.1.0/tests/test_ecg_data.py +113 -0
  47. atriakit-0.1.0/tests/test_ecg_dataset.py +113 -0
  48. atriakit-0.1.0/tests/test_features.py +1410 -0
  49. atriakit-0.1.0/tests/test_integration.py +209 -0
  50. atriakit-0.1.0/tests/test_loaders.py +126 -0
  51. atriakit-0.1.0/tests/test_morphology.py +72 -0
  52. atriakit-0.1.0/tests/test_pipeline.py +393 -0
  53. atriakit-0.1.0/tests/test_segment_processor.py +131 -0
  54. atriakit-0.1.0/tests/test_signal_preprocessor.py +278 -0
  55. atriakit-0.1.0/tests/test_utils.py +33 -0
atriakit-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nikolay Kormushev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.4
2
+ Name: atriakit
3
+ Version: 0.1.0
4
+ Summary: An open-source framework for reproducible P-wave phenotyping
5
+ Author-email: Nikolay Kormushev <nkormushev@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/nickormushev/AtriaKit
8
+ Project-URL: Repository, https://github.com/nickormushev/AtriaKit
9
+ Project-URL: Issues, https://github.com/nickormushev/AtriaKit/issues
10
+ Keywords: ecg,electrocardiogram,signal-processing,feature-extraction
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Requires-Python: >=3.12
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: numba>=0.57
23
+ Requires-Dist: numpy>=1.24
24
+ Requires-Dist: nolds>=0.5
25
+ Requires-Dist: pandas>=2.0
26
+ Requires-Dist: pydantic>=2.0
27
+ Requires-Dist: PyYAML>=6.0
28
+ Requires-Dist: pydicom>=2.4
29
+ Requires-Dist: scipy>=1.10
30
+ Requires-Dist: tqdm>=4.65
31
+ Provides-Extra: dev
32
+ Requires-Dist: neurokit2>=0.2.7; extra == "dev"
33
+ Requires-Dist: pytest>=7.4; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ <p align="center">
37
+ <img src="https://raw.githubusercontent.com/nickormushev/AtriaKit/main/docs/logo.png" alt="AtriaKit" height="90">
38
+ </p>
39
+
40
+ <p align="center">
41
+ An Open-Source Framework for Reproducible P-Wave Phenotyping
42
+ </p>
43
+
44
+ <p align="center">
45
+ <a href="https://github.com/nickormushev/AtriaKit/actions/workflows/tests.yml">
46
+ <img src="https://github.com/nickormushev/AtriaKit/actions/workflows/tests.yml/badge.svg" alt="Tests">
47
+ </a>
48
+ </p>
49
+
50
+ ## Quick Example
51
+
52
+ The fastest way to try AtriaKit hands-on is `examples/demo.ipynb`, which generates synthetic ECG data and runs it through the full pipeline end to end.
53
+
54
+ ```python
55
+ from atriakit import AnnotationsLoader, Pipeline
56
+
57
+ annotations = AnnotationsLoader().load("annotations.csv")
58
+ pipeline = Pipeline(ecg_base_path="path/to/dicom/root")
59
+
60
+ features = pipeline.run(annotations)
61
+ print(features.get_df().head())
62
+ ```
63
+
64
+ ## Highlights
65
+
66
+ - Temporal, morphological, nonlinear, and spatial (VCG) P-wave descriptors, each with an explicit mathematical definition; see the [manuscript](#citation)
67
+ - Paired with [`ecg_annotator`](https://github.com/nickormushev/AtriaKit/tree/main/ecg_annotator), an interactive tool for expert P-wave boundary annotation
68
+ - Configuration-driven: filtering, boundary mode, and baseline correction are explicit and reproducible, not hard-coded; tune them via config classes directly in Python or a single YAML file that maps to them
69
+ - Batch feature computation across recordings through a standardized `Pipeline` interface
70
+ - Unit-tested reusable components
71
+
72
+ Atrial fibrillation (AF) is a major cause of cardiovascular morbidity, and the P-wave of the surface electrocardiogram is an important non-invasive marker of atrial remodeling and conduction heterogeneity. In practice, however, P-wave analysis is often implemented through custom or ad hoc signal-processing pipelines, which makes results harder to reproduce across studies. General ECG processing libraries exist, but they often do not provide the more specialized atrial and P-wave metrics needed for high-resolution AF research.
73
+
74
+ AtriaKit addresses that gap with a modular feature-extraction library (see [Highlights](#highlights) above), built to scale from exploratory analysis to larger clinical datasets.
75
+
76
+ ## Implemented Feature Families
77
+
78
+ <p align="center">
79
+ <img src="https://raw.githubusercontent.com/nickormushev/AtriaKit/main/docs/Features.png" alt="Overview of AtriaKit's P-wave feature families" width="720">
80
+ </p>
81
+
82
+ The library currently includes:
83
+
84
+ - Duration and Dispersion
85
+ - Amplitude-Based P-Wave Features
86
+ - Area and Area-To-Duration Ratio
87
+ - P-Wave Terminal Force
88
+ - Morphology and Inflection-Point Features
89
+ - Fragmentation-Based Features
90
+ - Complexity
91
+ - Shannon Entropy and Sample Entropy
92
+ - Atrial Rate and Heart Rate
93
+ - Frontal and VCG Axis
94
+ - VCG Area and Eigenfeatures
95
+ - Signal-Averaged Vector-Magnitude P-Wave Amplitudes
96
+
97
+ The set is modular and can be extended with new metrics. For implementation details and mathematical definitions of each feature, see the AtriaKit manuscript ([Citation](#citation)).
98
+
99
+ ## Installation
100
+
101
+ ```bash
102
+ pip install atriakit
103
+ ```
104
+
105
+ To work from a clone of this repository instead (e.g. for development), install in editable mode:
106
+
107
+ ```bash
108
+ pip install -e .
109
+ ```
110
+
111
+ For development dependencies (testing, etc.):
112
+
113
+ ```bash
114
+ pip install -e .[dev]
115
+ ```
116
+
117
+ The package requires Python 3.12+.
118
+
119
+ ## Core API
120
+
121
+ The public package interface is exposed through `atriakit`:
122
+
123
+ ```python
124
+ from atriakit import (
125
+ Annotations,
126
+ AnnotationsLoader,
127
+ ECGData,
128
+ ECGDataset,
129
+ ECGLoader,
130
+ FeatureCalculators,
131
+ FeatureComputationConfig,
132
+ Pipeline,
133
+ PipelineConfig,
134
+ SegmentConfig,
135
+ SignalPreprocessor,
136
+ SignalPreprocessorConfig,
137
+ )
138
+ ```
139
+
140
+ Important components:
141
+
142
+ - `ECGLoader`: loads ECG waveforms from DICOM/IMA files and returns `ECGData`; register additional file types with `ECGLoader().register(extension, loader)`
143
+ - `ECGData`: holds the raw signal array plus lead layout, and returns preprocessed lead signals via a `SignalPreprocessor`
144
+ - `AnnotationsLoader`: loads a CSV or directory of annotation files into an `Annotations` object; also extensible via `.register(extension, loader)` for other annotation file formats
145
+ - `Annotations`: wrapper around the annotation table
146
+ - `FeatureCalculators`: computes ECG and VCG features for annotated segments, constructed with a `SegmentConfig`
147
+ - `SegmentConfig`: baseline correction mode and leading-sample skip applied to extracted segments
148
+ - `Pipeline`: runs batch feature extraction over a set of files, constructed with a `PipelineConfig`
149
+ - `PipelineConfig`: bundles everything needed to tune a `Pipeline` run: segment-boundary and baseline-correction settings, preprocessing filters for both feature and morphology signals, feature-computation parameters (via `FeatureComputationConfig`), and beat-grouping tolerance
150
+ - `FeatureComputationConfig`: per-call parameters for `FeatureCalculators.compute_all` (entropy, complexity, noise, morphology, and fragmentation settings)
151
+ - `SignalPreprocessor`: applies notch filtering, bandpass filtering, baseline correction, and normalization as a reusable preprocessing object; each step is optional and toggled independently via `SignalPreprocessorConfig`
152
+ - `ECGDataset`: computes per-lead mean/std across a set of files, for `SignalPreprocessorConfig`'s z-score normalization
153
+
154
+ ## Usage
155
+
156
+ `Pipeline` is the main entry point: it batches feature computation across many recordings at once, matching each file to its rows in a combined annotations table (e.g. one `annotations.csv` covering a whole directory of DICOM/IMA files). Every config object ships with reasonable defaults, as in the Quick Example above: `Pipeline(ecg_base_path=...)` works with no further configuration, but you can tune it to your needs via a YAML file or by building the config objects directly in Python:
157
+
158
+ ### 1. Configure via YAML (recommended)
159
+
160
+ Rather than building `PipelineConfig`/`SignalPreprocessorConfig` objects by hand, point `Pipeline.from_yaml` at a config file. See `examples/pipeline_config.yaml` for the full reference, covering paths, annotation boundary mode, preprocessing filters, and pipeline parameters (abbreviated below):
161
+
162
+ ```yaml
163
+ ecg_base_path: ./data
164
+ annotations_dir: ./data
165
+ pipeline:
166
+ group_tolerance_ms: 200
167
+ signal_preprocessor_config:
168
+ lowcut: 1
169
+ highcut: 40
170
+ notch_freq: 50.0
171
+ feature_computation:
172
+ shannon_entropy_n_bins: 128
173
+ ```
174
+
175
+ ```python
176
+ pipeline, annotations = Pipeline.from_yaml("pipeline_config.yaml")
177
+ result = pipeline.run(annotations)
178
+ ```
179
+
180
+ Required annotation columns: `file_path`, `lead`, `onset`, `offset`, `p_wave_id`. Optional columns such as `type`, `patient_id`, and `ignore` are used when available.
181
+
182
+ ### 2. Or configure directly in Python
183
+
184
+ For programmatic control (e.g. sweeping parameters in a script), pass a `PipelineConfig` straight to `Pipeline`:
185
+
186
+ ```python
187
+ from atriakit import FeatureComputationConfig, Pipeline, PipelineConfig, SignalPreprocessorConfig
188
+
189
+ pipeline = Pipeline(
190
+ ecg_base_path="path/to/dicom/root",
191
+ pipeline_config=PipelineConfig(
192
+ signal_preprocessor_config=SignalPreprocessorConfig(
193
+ lowcut=1.0, highcut=40.0, notch_freq=50.0
194
+ ),
195
+ morphology_preprocessor_config=SignalPreprocessorConfig(
196
+ lowcut=1.0, highcut=30.0, notch_freq=50.0
197
+ ),
198
+ feature_computation=FeatureComputationConfig(shannon_entropy_n_bins=128),
199
+ ),
200
+ )
201
+ ```
202
+
203
+ ### 3. Compute features for one recording directly
204
+
205
+ Unlike `Pipeline`, `FeatureCalculators` works on a single already-loaded recording, so its annotations must be pre-filtered to that one file, since onset/offset are sample indices into that specific signal:
206
+
207
+ ```python
208
+ from atriakit import AnnotationsLoader, ECGLoader, FeatureCalculators
209
+
210
+ ecg_data = ECGLoader().load("path/to/file.IMA")
211
+ annotations = AnnotationsLoader().load("annotations.csv")
212
+ file_annotations = annotations[annotations["file_path"] == "path/to/file.IMA"]
213
+
214
+ fc = FeatureCalculators()
215
+
216
+ area = fc.area(file_annotations, ecg_data)
217
+ max_amplitude = fc.max_amplitude(file_annotations, ecg_data)
218
+ all_features = fc.compute_all(file_annotations, ecg_data)
219
+ ```
220
+
221
+ ## Preprocessing
222
+
223
+ Signal preprocessing is intentionally separated from feature extraction.
224
+
225
+ - filter settings live in `SignalPreprocessorConfig`
226
+ - runtime normalization state (`mean` / `std`) lives on `SignalPreprocessor`
227
+ - `ECGData` delegates preprocessing to a `SignalPreprocessor`
228
+ - `FeatureCalculators` can use separate preprocessors for standard signal features and morphology-specific features
229
+
230
+ Example:
231
+
232
+ ```python
233
+ from atriakit import ECGLoader, FeatureCalculators, SignalPreprocessor, SignalPreprocessorConfig
234
+
235
+ preprocessor = SignalPreprocessor(
236
+ SignalPreprocessorConfig(lowcut=1.0, highcut=40.0, notch_freq=50.0)
237
+ )
238
+
239
+ ecg_data = ECGLoader().load("path/to/file.IMA")
240
+ lead_ii = ecg_data.get_lead_signal("II", preprocessor=preprocessor)
241
+
242
+ # FeatureCalculators takes the same kind of preprocessor, separately for
243
+ # standard signal features and morphology-specific features (both optional;
244
+ # see Core API above for the defaults used when omitted)
245
+ fc = FeatureCalculators(
246
+ signal_preprocessor=preprocessor,
247
+ morphology_preprocessor=SignalPreprocessor(
248
+ SignalPreprocessorConfig(lowcut=1.0, highcut=30.0, notch_freq=50.0)
249
+ ),
250
+ )
251
+ ```
252
+
253
+ ## Repository Layout
254
+
255
+ ```text
256
+ AtriaKit/
257
+ ├── atriakit/
258
+ │ ├── __init__.py
259
+ │ ├── datasets.py
260
+ │ ├── feature_calculator.py
261
+ │ ├── utils.py
262
+ │ ├── configs/
263
+ │ ├── features/
264
+ │ ├── io/
265
+ │ ├── models/
266
+ │ ├── preprocessing/
267
+ │ └── processing/
268
+ ├── examples/
269
+ ├── tests/
270
+ ├── ecg_annotator/ # companion desktop annotation tool (separate package)
271
+ └── pyproject.toml
272
+ ```
273
+
274
+ Main directories:
275
+
276
+ - `atriakit/`: reusable ECG feature extraction package
277
+ - `atriakit/io/`: DICOM/ECG loading, annotation-file loading, and waveform access
278
+ - `atriakit/models/`: `ECGData`, `Annotations`, and the annotation column schema
279
+ - `atriakit/preprocessing/`: signal and annotation preprocessing
280
+ - `atriakit/processing/`: dataset utilities and the batch pipeline
281
+ - `atriakit/features/`: individual feature implementations
282
+ - `atriakit/configs/`: processing and feature configuration
283
+ - `examples/`: demo material
284
+ - `tests/`: unit tests
285
+ - `ecg_annotator/`: standalone PyQt6 desktop app for manually annotating P-waves; a separate, independently packaged project, see its own [README](https://github.com/nickormushev/AtriaKit/blob/main/ecg_annotator/README.md)
286
+
287
+ ## Testing
288
+
289
+ Run the test suite with:
290
+
291
+ ```bash
292
+ pytest
293
+ ```
294
+
295
+ Or run a specific test module:
296
+
297
+ ```bash
298
+ pytest tests/test_features.py -q
299
+ ```
300
+
301
+ ## Notes
302
+
303
+ The repository favors a pragmatic research-code style, but the core package is structured to support gradual cleanup, extension, and more standardized use over time.
304
+
305
+ ## Citation
306
+
307
+ <!-- TODO: fill in once the paper is public -->
308
+
309
+ If you use AtriaKit in your research, please cite:
310
+
311
+ ```bibtex
312
+ @software{atriakit,
313
+ author = {Kormushev, Nikolay},
314
+ title = {AtriaKit: An Open-Source Framework for Reproducible P-Wave Phenotyping},
315
+ year = {2026},
316
+ url = {https://github.com/nickormushev/AtriaKit}
317
+ }
318
+ ```
319
+
320
+ ## License
321
+
322
+ MIT, see [LICENSE](https://github.com/nickormushev/AtriaKit/blob/main/LICENSE).
323
+
324
+ ## Development Ideas
325
+
326
+ Areas that would likely improve maintainability further:
327
+
328
+ - split `feature_calculator.py` into smaller focused modules over time
329
+ - expand examples and API documentation
330
+ - add parallelisation
@@ -0,0 +1,295 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/nickormushev/AtriaKit/main/docs/logo.png" alt="AtriaKit" height="90">
3
+ </p>
4
+
5
+ <p align="center">
6
+ An Open-Source Framework for Reproducible P-Wave Phenotyping
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://github.com/nickormushev/AtriaKit/actions/workflows/tests.yml">
11
+ <img src="https://github.com/nickormushev/AtriaKit/actions/workflows/tests.yml/badge.svg" alt="Tests">
12
+ </a>
13
+ </p>
14
+
15
+ ## Quick Example
16
+
17
+ The fastest way to try AtriaKit hands-on is `examples/demo.ipynb`, which generates synthetic ECG data and runs it through the full pipeline end to end.
18
+
19
+ ```python
20
+ from atriakit import AnnotationsLoader, Pipeline
21
+
22
+ annotations = AnnotationsLoader().load("annotations.csv")
23
+ pipeline = Pipeline(ecg_base_path="path/to/dicom/root")
24
+
25
+ features = pipeline.run(annotations)
26
+ print(features.get_df().head())
27
+ ```
28
+
29
+ ## Highlights
30
+
31
+ - Temporal, morphological, nonlinear, and spatial (VCG) P-wave descriptors, each with an explicit mathematical definition; see the [manuscript](#citation)
32
+ - Paired with [`ecg_annotator`](https://github.com/nickormushev/AtriaKit/tree/main/ecg_annotator), an interactive tool for expert P-wave boundary annotation
33
+ - Configuration-driven: filtering, boundary mode, and baseline correction are explicit and reproducible, not hard-coded; tune them via config classes directly in Python or a single YAML file that maps to them
34
+ - Batch feature computation across recordings through a standardized `Pipeline` interface
35
+ - Unit-tested reusable components
36
+
37
+ Atrial fibrillation (AF) is a major cause of cardiovascular morbidity, and the P-wave of the surface electrocardiogram is an important non-invasive marker of atrial remodeling and conduction heterogeneity. In practice, however, P-wave analysis is often implemented through custom or ad hoc signal-processing pipelines, which makes results harder to reproduce across studies. General ECG processing libraries exist, but they often do not provide the more specialized atrial and P-wave metrics needed for high-resolution AF research.
38
+
39
+ AtriaKit addresses that gap with a modular feature-extraction library (see [Highlights](#highlights) above), built to scale from exploratory analysis to larger clinical datasets.
40
+
41
+ ## Implemented Feature Families
42
+
43
+ <p align="center">
44
+ <img src="https://raw.githubusercontent.com/nickormushev/AtriaKit/main/docs/Features.png" alt="Overview of AtriaKit's P-wave feature families" width="720">
45
+ </p>
46
+
47
+ The library currently includes:
48
+
49
+ - Duration and Dispersion
50
+ - Amplitude-Based P-Wave Features
51
+ - Area and Area-To-Duration Ratio
52
+ - P-Wave Terminal Force
53
+ - Morphology and Inflection-Point Features
54
+ - Fragmentation-Based Features
55
+ - Complexity
56
+ - Shannon Entropy and Sample Entropy
57
+ - Atrial Rate and Heart Rate
58
+ - Frontal and VCG Axis
59
+ - VCG Area and Eigenfeatures
60
+ - Signal-Averaged Vector-Magnitude P-Wave Amplitudes
61
+
62
+ The set is modular and can be extended with new metrics. For implementation details and mathematical definitions of each feature, see the AtriaKit manuscript ([Citation](#citation)).
63
+
64
+ ## Installation
65
+
66
+ ```bash
67
+ pip install atriakit
68
+ ```
69
+
70
+ To work from a clone of this repository instead (e.g. for development), install in editable mode:
71
+
72
+ ```bash
73
+ pip install -e .
74
+ ```
75
+
76
+ For development dependencies (testing, etc.):
77
+
78
+ ```bash
79
+ pip install -e .[dev]
80
+ ```
81
+
82
+ The package requires Python 3.12+.
83
+
84
+ ## Core API
85
+
86
+ The public package interface is exposed through `atriakit`:
87
+
88
+ ```python
89
+ from atriakit import (
90
+ Annotations,
91
+ AnnotationsLoader,
92
+ ECGData,
93
+ ECGDataset,
94
+ ECGLoader,
95
+ FeatureCalculators,
96
+ FeatureComputationConfig,
97
+ Pipeline,
98
+ PipelineConfig,
99
+ SegmentConfig,
100
+ SignalPreprocessor,
101
+ SignalPreprocessorConfig,
102
+ )
103
+ ```
104
+
105
+ Important components:
106
+
107
+ - `ECGLoader`: loads ECG waveforms from DICOM/IMA files and returns `ECGData`; register additional file types with `ECGLoader().register(extension, loader)`
108
+ - `ECGData`: holds the raw signal array plus lead layout, and returns preprocessed lead signals via a `SignalPreprocessor`
109
+ - `AnnotationsLoader`: loads a CSV or directory of annotation files into an `Annotations` object; also extensible via `.register(extension, loader)` for other annotation file formats
110
+ - `Annotations`: wrapper around the annotation table
111
+ - `FeatureCalculators`: computes ECG and VCG features for annotated segments, constructed with a `SegmentConfig`
112
+ - `SegmentConfig`: baseline correction mode and leading-sample skip applied to extracted segments
113
+ - `Pipeline`: runs batch feature extraction over a set of files, constructed with a `PipelineConfig`
114
+ - `PipelineConfig`: bundles everything needed to tune a `Pipeline` run: segment-boundary and baseline-correction settings, preprocessing filters for both feature and morphology signals, feature-computation parameters (via `FeatureComputationConfig`), and beat-grouping tolerance
115
+ - `FeatureComputationConfig`: per-call parameters for `FeatureCalculators.compute_all` (entropy, complexity, noise, morphology, and fragmentation settings)
116
+ - `SignalPreprocessor`: applies notch filtering, bandpass filtering, baseline correction, and normalization as a reusable preprocessing object; each step is optional and toggled independently via `SignalPreprocessorConfig`
117
+ - `ECGDataset`: computes per-lead mean/std across a set of files, for `SignalPreprocessorConfig`'s z-score normalization
118
+
119
+ ## Usage
120
+
121
+ `Pipeline` is the main entry point: it batches feature computation across many recordings at once, matching each file to its rows in a combined annotations table (e.g. one `annotations.csv` covering a whole directory of DICOM/IMA files). Every config object ships with reasonable defaults, as in the Quick Example above: `Pipeline(ecg_base_path=...)` works with no further configuration, but you can tune it to your needs via a YAML file or by building the config objects directly in Python:
122
+
123
+ ### 1. Configure via YAML (recommended)
124
+
125
+ Rather than building `PipelineConfig`/`SignalPreprocessorConfig` objects by hand, point `Pipeline.from_yaml` at a config file. See `examples/pipeline_config.yaml` for the full reference, covering paths, annotation boundary mode, preprocessing filters, and pipeline parameters (abbreviated below):
126
+
127
+ ```yaml
128
+ ecg_base_path: ./data
129
+ annotations_dir: ./data
130
+ pipeline:
131
+ group_tolerance_ms: 200
132
+ signal_preprocessor_config:
133
+ lowcut: 1
134
+ highcut: 40
135
+ notch_freq: 50.0
136
+ feature_computation:
137
+ shannon_entropy_n_bins: 128
138
+ ```
139
+
140
+ ```python
141
+ pipeline, annotations = Pipeline.from_yaml("pipeline_config.yaml")
142
+ result = pipeline.run(annotations)
143
+ ```
144
+
145
+ Required annotation columns: `file_path`, `lead`, `onset`, `offset`, `p_wave_id`. Optional columns such as `type`, `patient_id`, and `ignore` are used when available.
146
+
147
+ ### 2. Or configure directly in Python
148
+
149
+ For programmatic control (e.g. sweeping parameters in a script), pass a `PipelineConfig` straight to `Pipeline`:
150
+
151
+ ```python
152
+ from atriakit import FeatureComputationConfig, Pipeline, PipelineConfig, SignalPreprocessorConfig
153
+
154
+ pipeline = Pipeline(
155
+ ecg_base_path="path/to/dicom/root",
156
+ pipeline_config=PipelineConfig(
157
+ signal_preprocessor_config=SignalPreprocessorConfig(
158
+ lowcut=1.0, highcut=40.0, notch_freq=50.0
159
+ ),
160
+ morphology_preprocessor_config=SignalPreprocessorConfig(
161
+ lowcut=1.0, highcut=30.0, notch_freq=50.0
162
+ ),
163
+ feature_computation=FeatureComputationConfig(shannon_entropy_n_bins=128),
164
+ ),
165
+ )
166
+ ```
167
+
168
+ ### 3. Compute features for one recording directly
169
+
170
+ Unlike `Pipeline`, `FeatureCalculators` works on a single already-loaded recording, so its annotations must be pre-filtered to that one file, since onset/offset are sample indices into that specific signal:
171
+
172
+ ```python
173
+ from atriakit import AnnotationsLoader, ECGLoader, FeatureCalculators
174
+
175
+ ecg_data = ECGLoader().load("path/to/file.IMA")
176
+ annotations = AnnotationsLoader().load("annotations.csv")
177
+ file_annotations = annotations[annotations["file_path"] == "path/to/file.IMA"]
178
+
179
+ fc = FeatureCalculators()
180
+
181
+ area = fc.area(file_annotations, ecg_data)
182
+ max_amplitude = fc.max_amplitude(file_annotations, ecg_data)
183
+ all_features = fc.compute_all(file_annotations, ecg_data)
184
+ ```
185
+
186
+ ## Preprocessing
187
+
188
+ Signal preprocessing is intentionally separated from feature extraction.
189
+
190
+ - filter settings live in `SignalPreprocessorConfig`
191
+ - runtime normalization state (`mean` / `std`) lives on `SignalPreprocessor`
192
+ - `ECGData` delegates preprocessing to a `SignalPreprocessor`
193
+ - `FeatureCalculators` can use separate preprocessors for standard signal features and morphology-specific features
194
+
195
+ Example:
196
+
197
+ ```python
198
+ from atriakit import ECGLoader, FeatureCalculators, SignalPreprocessor, SignalPreprocessorConfig
199
+
200
+ preprocessor = SignalPreprocessor(
201
+ SignalPreprocessorConfig(lowcut=1.0, highcut=40.0, notch_freq=50.0)
202
+ )
203
+
204
+ ecg_data = ECGLoader().load("path/to/file.IMA")
205
+ lead_ii = ecg_data.get_lead_signal("II", preprocessor=preprocessor)
206
+
207
+ # FeatureCalculators takes the same kind of preprocessor, separately for
208
+ # standard signal features and morphology-specific features (both optional;
209
+ # see Core API above for the defaults used when omitted)
210
+ fc = FeatureCalculators(
211
+ signal_preprocessor=preprocessor,
212
+ morphology_preprocessor=SignalPreprocessor(
213
+ SignalPreprocessorConfig(lowcut=1.0, highcut=30.0, notch_freq=50.0)
214
+ ),
215
+ )
216
+ ```
217
+
218
+ ## Repository Layout
219
+
220
+ ```text
221
+ AtriaKit/
222
+ ├── atriakit/
223
+ │ ├── __init__.py
224
+ │ ├── datasets.py
225
+ │ ├── feature_calculator.py
226
+ │ ├── utils.py
227
+ │ ├── configs/
228
+ │ ├── features/
229
+ │ ├── io/
230
+ │ ├── models/
231
+ │ ├── preprocessing/
232
+ │ └── processing/
233
+ ├── examples/
234
+ ├── tests/
235
+ ├── ecg_annotator/ # companion desktop annotation tool (separate package)
236
+ └── pyproject.toml
237
+ ```
238
+
239
+ Main directories:
240
+
241
+ - `atriakit/`: reusable ECG feature extraction package
242
+ - `atriakit/io/`: DICOM/ECG loading, annotation-file loading, and waveform access
243
+ - `atriakit/models/`: `ECGData`, `Annotations`, and the annotation column schema
244
+ - `atriakit/preprocessing/`: signal and annotation preprocessing
245
+ - `atriakit/processing/`: dataset utilities and the batch pipeline
246
+ - `atriakit/features/`: individual feature implementations
247
+ - `atriakit/configs/`: processing and feature configuration
248
+ - `examples/`: demo material
249
+ - `tests/`: unit tests
250
+ - `ecg_annotator/`: standalone PyQt6 desktop app for manually annotating P-waves; a separate, independently packaged project, see its own [README](https://github.com/nickormushev/AtriaKit/blob/main/ecg_annotator/README.md)
251
+
252
+ ## Testing
253
+
254
+ Run the test suite with:
255
+
256
+ ```bash
257
+ pytest
258
+ ```
259
+
260
+ Or run a specific test module:
261
+
262
+ ```bash
263
+ pytest tests/test_features.py -q
264
+ ```
265
+
266
+ ## Notes
267
+
268
+ The repository favors a pragmatic research-code style, but the core package is structured to support gradual cleanup, extension, and more standardized use over time.
269
+
270
+ ## Citation
271
+
272
+ <!-- TODO: fill in once the paper is public -->
273
+
274
+ If you use AtriaKit in your research, please cite:
275
+
276
+ ```bibtex
277
+ @software{atriakit,
278
+ author = {Kormushev, Nikolay},
279
+ title = {AtriaKit: An Open-Source Framework for Reproducible P-Wave Phenotyping},
280
+ year = {2026},
281
+ url = {https://github.com/nickormushev/AtriaKit}
282
+ }
283
+ ```
284
+
285
+ ## License
286
+
287
+ MIT, see [LICENSE](https://github.com/nickormushev/AtriaKit/blob/main/LICENSE).
288
+
289
+ ## Development Ideas
290
+
291
+ Areas that would likely improve maintainability further:
292
+
293
+ - split `feature_calculator.py` into smaller focused modules over time
294
+ - expand examples and API documentation
295
+ - add parallelisation