ecg-plotter 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: ecg-plotter
3
+ Version: 0.1.0
4
+ Summary: Research/visualisation ECG sheets at settable size and DPI (not for diagnostic use).
5
+ Author-email: "Jarod P. Hartley" <jarodph.contact@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/JarodPH/ecg-plotter
8
+ Project-URL: Repository, https://github.com/JarodPH/ecg-plotter
9
+ Project-URL: Zenodo, https://doi.org/10.5281/zenodo.23984258
10
+ Project-URL: ORCID, https://orcid.org/0009-0006-0736-5100
11
+ Keywords: ecg,electrocardiogram,plot,wfdb
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
21
+ Classifier: Topic :: Scientific/Engineering :: Visualization
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: numpy>=1.24
26
+ Requires-Dist: pandas>=2.0
27
+ Requires-Dist: scipy>=1.10
28
+ Requires-Dist: matplotlib>=3.7
29
+ Requires-Dist: Pillow>=9.0
30
+ Requires-Dist: wfdb>=4.1
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=7.0; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # ECG Plotter
36
+
37
+ Plot 12-lead ECGs as classic clinical-style sheets with **settable physical size or pixel resolution**, **DPI**, optional **signal filters**, and accurate **mm/s / mm/mV** footer reporting.
38
+
39
+ **Research / visualisation only — not for diagnostic use.** This package renders ECG traces for research, teaching, and ML dataset creation. It is not a medical device and must not be used for clinical diagnosis or patient care.
40
+
41
+ **Author:** Jarod P. Hartley ([ORCID](https://orcid.org/0009-0006-0736-5100)), Swansea University — jarodph.contact@gmail.com
42
+
43
+ ![Colour sheet, 9.84 by 6.3 inches at 300 DPI](examples/output/size_colour_inches.png)
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install ecg-plotter
49
+ ```
50
+
51
+ Dependencies, installed automatically: `numpy`, `pandas`, `scipy`, `matplotlib`, `Pillow`, `wfdb`.
52
+
53
+ ## Quick start
54
+
55
+ ```python
56
+ import ecg_plotter as ecg
57
+
58
+ data = ecg.load_wfdb("path/to/record") # without .hea/.dat extension
59
+ leads, fs = data["leads"], data["fs"]
60
+
61
+ # Option A: filter inside plot (notch/bandpass from the same kwargs)
62
+ ecg.plot(
63
+ leads,
64
+ "out.png",
65
+ inches=(9.84, 6.3), # or resolution=(2952, 1890)
66
+ dpi=300, # default 300
67
+ fs=fs,
68
+ colour=True, # pink RGB paper; False → 1-channel greyscale
69
+ id=data["record_name"],
70
+ anonymise=True,
71
+ filter=True, # apply notch/bandpass; False → footer text only
72
+ notch_hz=60,
73
+ bandpass_hz=(0.05, 150),
74
+ )
75
+
76
+ # Option B: filter yourself first (median / custom chains)
77
+ # leads = ecg.apply_to_leads(leads, ecg.median_baseline, kernel_size=201)
78
+ # ecg.plot(leads, "out.png", ..., filter=False, notch_hz=60, bandpass_hz=(0.05, 150))
79
+ ```
80
+
81
+ ## Size and calibration
82
+
83
+ Provide **either** `inches=(width, height)` **or** `resolution=(width_px, height_px)`.
84
+
85
+ - Resolution → inches via `pixels / dpi`
86
+ - Default DPI is **300**
87
+ - Footer speed / gain are computed from the **saved** geometry:
88
+
89
+ ```text
90
+ duration_s = n_samples / fs
91
+ speed_mm/s = (width_px / dpi) * 25.4 / duration_s
92
+ gain_mm/mV = (height_px / dpi) * 25.4 / 16
93
+ ```
94
+
95
+ At a fixed recording length, a larger page increases mm/s. A taller page increases mm/mV. The footer prints those computed values.
96
+
97
+ ## Filters
98
+
99
+ | Function / option | Role |
100
+ |---|---|
101
+ | `plot(..., filter=True)` | Apply `notch_hz` / `bandpass_hz` inside `plot`; with `filter=False` those kwargs only set the footer text |
102
+ | `bandpass` | Butterworth band-pass (default 0.05–150 Hz) |
103
+ | `notch` | IIR mains notch (50/60 Hz) |
104
+ | `median_baseline` | Median-filter baseline subtraction |
105
+ | `apply_to_leads` | Map any 1-D filter across leads (dict / array / DataFrame) |
106
+
107
+ Notch and band-pass run forward and back, so the QRS is not shifted in time. The default band-pass needs more than about 28 samples. A shorter trace raises `ValueError`.
108
+
109
+ ## Colour vs greyscale
110
+
111
+ - `colour=True` → pink ECG-paper grid
112
+ - `colour=False` → greyscale grid
113
+ - PNG colour is RGB (3 channels); PNG greyscale is mode `L` (1 channel)
114
+
115
+ ## Output formats
116
+
117
+ `plot` writes **PNG**, **PDF**, or **SVG** (inferred from the file extension, or set `format=`):
118
+
119
+ ```python
120
+ ecg.plot(leads, "sheet.pdf", inches=(9.84, 6.3), fs=fs) # vector PDF
121
+ ecg.plot(leads, "sheet.svg", inches=(9.84, 6.3), fs=fs)
122
+ ecg.plot(leads, "sheet.png", resolution=(2952, 1890), dpi=300, fs=fs)
123
+ ```
124
+
125
+ ## CLI
126
+
127
+ ```bash
128
+ ecg-plotter path/to/record -o out.pdf --inches 9.84 6.3 --anonymise --filter --notch-hz 60 --bandpass-hz 0.05 150
129
+ ecg-plotter --jsonl examples/data/synthetic_data.jsonl --index 0 -o out.svg --greyscale
130
+ ecg-plotter --version
131
+ ```
132
+
133
+ ## Anonymisation
134
+
135
+ For ML dataset creation, set `anonymise=True` so the footer ID is always the fixed placeholder `15030416` instead of a real record ID (which models can overfit on).
136
+
137
+ ## Input formats
138
+
139
+ Amplitudes are in millivolts. `load_wfdb` reads WFDB `p_signal`.
140
+
141
+ `plot` and `apply_to_leads` accept:
142
+
143
+ - `dict[str, array]` with lead names (`I`, `II`, … `V6`)
144
+ - `ndarray` of shape `(12, n)` or `(n, 12)` (default name order: `LEAD_NAMES`)
145
+ - `pandas.DataFrame` with lead columns:
146
+ - **wide**: rows = samples, columns = lead names
147
+ - **single-row**: one row whose lead cells contain arrays/lists (e.g. from JSONL)
148
+ - **12 columns and no lead names**: the columns are taken in order as `LEAD_NAMES`, and a warning is issued. Pass `lead_names=` if that order is wrong. Any other unlabelled table is rejected.
149
+
150
+ ```python
151
+ import pandas as pd
152
+
153
+ df = pd.DataFrame(
154
+ {
155
+ "I": signal["I"],
156
+ "II": signal["II"],
157
+ "III": signal["III"],
158
+ "aVR": signal["aVR"],
159
+ "aVL": signal["aVL"],
160
+ "aVF": signal["aVF"],
161
+ "V1": signal["V1"],
162
+ "V2": signal["V2"],
163
+ "V3": signal["V3"],
164
+ "V4": signal["V4"],
165
+ "V5": signal["V5"],
166
+ "V6": signal["V6"],
167
+ }
168
+ ) # shape: (n_samples, 12)
169
+ ecg.plot(df, "out.png", inches=(9.84, 6.3), fs=500)
170
+ ```
171
+
172
+ JSON Lines records from `load_jsonl` must include a positive ``fs`` field (Hz). The CLI uses that value unless ``--fs`` overrides it.
173
+
174
+ ## Layout
175
+
176
+ Classic sheet:
177
+
178
+ ```text
179
+ I aVR V1 V4
180
+ II aVL V2 V5
181
+ III aVF V3 V6
182
+ ─── II rhythm ────
183
+ ```
184
+
185
+ Column width and sample count scale with the recording length (split into four equal segments). If the sample count is not divisible by 4, the last 1–3 samples are dropped so every column has the same duration, and a warning is raised. Sampling rate drives grid spacing (major ≈ 0.2 s).
186
+
187
+ ## Examples
188
+
189
+ Sample data in `examples/data/` is **synthetic only** (no real patient recordings).
190
+ Regenerate with `python examples/data/generate_synthetic.py`.
191
+
192
+ ```bash
193
+ python examples/plot_from_wfdb.py
194
+ python examples/plot_from_dataframe.py
195
+ python examples/plot_from_jsonl.py
196
+ python examples/plot_colour_and_greyscale.py
197
+ python examples/plot_low_dpi.py
198
+ python examples/plot_nonstandard_calibration.py
199
+ python examples/plot_with_standalone_filters.py
200
+ python examples/plot_pdf_and_svg.py
201
+ ```
202
+
203
+ | Script | Shows |
204
+ |---|---|
205
+ | `plot_from_wfdb.py` | Load synthetic WFDB `synth_0001`, filter inside `plot`, colour PNG |
206
+ | `plot_from_dataframe.py` | Build an explicit lead DataFrame and plot it |
207
+ | `plot_from_jsonl.py` | Load the first record from `synthetic_data.jsonl` |
208
+ | `plot_colour_and_greyscale.py` | Inches vs resolution, colour vs greyscale |
209
+ | `plot_low_dpi.py` | Same physical size at 150 DPI (smaller pixel dimensions) |
210
+ | `plot_nonstandard_calibration.py` | Non-standard sizes. Footers report about 50 mm/s with 5 mm/mV, and about 20 mm/s with 12.5 mm/mV |
211
+ | `plot_with_standalone_filters.py` | Filter with `apply_to_leads`, then `plot(..., filter=False)` |
212
+ | `plot_pdf_and_svg.py` | Vector outputs: colour PDF and greyscale SVG |
213
+
214
+ Outputs are written to `examples/output/`.
215
+
216
+ ![Greyscale sheet, same page size](examples/output/size_grey_resolution.png)
217
+
218
+ ## How to cite
219
+
220
+ If you use this software, please cite it:
221
+
222
+ Hartley, J. P. (2026). *ecg-plotter*. Zenodo. https://doi.org/10.5281/zenodo.23984258
223
+
224
+ ## Tests
225
+
226
+ ```bash
227
+ pip install -e ".[dev]"
228
+ pytest
229
+ ```
230
+
231
+ GitHub Actions runs these commands on Python 3.9, 3.11, and 3.12.
@@ -0,0 +1,14 @@
1
+ ecg_plotter/__init__.py,sha256=kL689_cpY1FkW2K0B8PUZj_t3eCNhcTZvlMLHNFjoHA,2105
2
+ ecg_plotter/_version.py,sha256=knQKxvGwnF44uSfZKQkMiaOWmbeuRdy3ZufsXR0mzR8,102
3
+ ecg_plotter/cli.py,sha256=xhJyUg4uGWaZ9uSdbzTnEfJOobBD-JrJVaydlBHHcas,5500
4
+ ecg_plotter/filters.py,sha256=YAb3hFiVYbdacPFrKHKwklEQxk5dfhrMm0UAmPT4Nsk,4362
5
+ ecg_plotter/io.py,sha256=1X-udVknOYLlIBRjqwXfBqHDkj40DcGytUPaERzk80U,5972
6
+ ecg_plotter/leads.py,sha256=jhcqWZeEcd7qtMVXR-zABLuqse9RbNELYa66x3kZn_c,6576
7
+ ecg_plotter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ ecg_plotter/sheet.py,sha256=Yy6rH8JQWrITfcJdnJXFHQ0vOMaf48TjEFG5uGWonug,17082
9
+ ecg_plotter-0.1.0.dist-info/licenses/LICENSE,sha256=IbH7MZBfprI1Damp5Vik_yrfLm86oBfIuyIwRq-Wp-A,1094
10
+ ecg_plotter-0.1.0.dist-info/METADATA,sha256=7ubitgf9y6IMe65qG4yYS9dCbCe6XNmaIAhXwIAzSkI,8604
11
+ ecg_plotter-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
12
+ ecg_plotter-0.1.0.dist-info/entry_points.txt,sha256=7g_TUueuGyVpI-00nO-O2NiRl7RXVpC5Lz7Ecj1iGOY,53
13
+ ecg_plotter-0.1.0.dist-info/top_level.txt,sha256=8sPikcrFcXuMurshE3XFlz--dKai2xYxyShaY8sXZsE,12
14
+ ecg_plotter-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ecg-plotter = ecg_plotter.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jarod P. Hartley
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 @@
1
+ ecg_plotter