pmecg 0.2.3__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 (84) hide show
  1. pmecg-0.2.3/.github/copilot-instructions.md +63 -0
  2. pmecg-0.2.3/.github/workflows/ci.yml +95 -0
  3. pmecg-0.2.3/.github/workflows/release.yml +95 -0
  4. pmecg-0.2.3/.gitignore +40 -0
  5. pmecg-0.2.3/.python-version +1 -0
  6. pmecg-0.2.3/.readthedocs.yml +14 -0
  7. pmecg-0.2.3/LICENSE +674 -0
  8. pmecg-0.2.3/PKG-INFO +83 -0
  9. pmecg-0.2.3/README.md +51 -0
  10. pmecg-0.2.3/assets/logo-dark.svg +16 -0
  11. pmecg-0.2.3/assets/logo.svg +16 -0
  12. pmecg-0.2.3/docs/_config.yml +69 -0
  13. pmecg-0.2.3/docs/_toc.yml +14 -0
  14. pmecg-0.2.3/docs/api/index.md +32 -0
  15. pmecg-0.2.3/docs/examples/attention.md +238 -0
  16. pmecg-0.2.3/docs/examples/basic.md +72 -0
  17. pmecg-0.2.3/docs/examples/configurations.md +202 -0
  18. pmecg-0.2.3/docs/examples/index.md +5 -0
  19. pmecg-0.2.3/docs/intro.md +56 -0
  20. pmecg-0.2.3/example/artifacts/attention/4x3-background-positive.pdf +0 -0
  21. pmecg-0.2.3/example/artifacts/attention/4x3-background-positive.png +0 -0
  22. pmecg-0.2.3/example/artifacts/attention/4x3-background-signed.pdf +0 -0
  23. pmecg-0.2.3/example/artifacts/attention/4x3-background-signed.png +0 -0
  24. pmecg-0.2.3/example/artifacts/attention/4x3-interval-positive.pdf +0 -0
  25. pmecg-0.2.3/example/artifacts/attention/4x3-interval-positive.png +0 -0
  26. pmecg-0.2.3/example/artifacts/attention/4x3-interval-signed.pdf +0 -0
  27. pmecg-0.2.3/example/artifacts/attention/4x3-interval-signed.png +0 -0
  28. pmecg-0.2.3/example/artifacts/attention/4x3-line-color-positive.pdf +0 -0
  29. pmecg-0.2.3/example/artifacts/attention/4x3-line-color-positive.png +0 -0
  30. pmecg-0.2.3/example/artifacts/attention/4x3-line-color-signed.pdf +0 -0
  31. pmecg-0.2.3/example/artifacts/attention/4x3-line-color-signed.png +0 -0
  32. pmecg-0.2.3/example/artifacts/attention/manual-background.pdf +0 -0
  33. pmecg-0.2.3/example/artifacts/attention/manual-background.png +0 -0
  34. pmecg-0.2.3/example/artifacts/attention/manual-interval.pdf +0 -0
  35. pmecg-0.2.3/example/artifacts/attention/manual-interval.png +0 -0
  36. pmecg-0.2.3/example/artifacts/attention/manual-line-color.pdf +0 -0
  37. pmecg-0.2.3/example/artifacts/attention/manual-line-color.png +0 -0
  38. pmecg-0.2.3/example/artifacts/no-attention/1/1x3.pdf +0 -0
  39. pmecg-0.2.3/example/artifacts/no-attention/1/1x3.png +0 -0
  40. pmecg-0.2.3/example/artifacts/no-attention/1/2x6.pdf +0 -0
  41. pmecg-0.2.3/example/artifacts/no-attention/1/2x6.png +0 -0
  42. pmecg-0.2.3/example/artifacts/no-attention/1/4x3.pdf +0 -0
  43. pmecg-0.2.3/example/artifacts/no-attention/1/4x3.png +0 -0
  44. pmecg-0.2.3/example/artifacts/no-attention/2/1x3.pdf +0 -0
  45. pmecg-0.2.3/example/artifacts/no-attention/2/1x3.png +0 -0
  46. pmecg-0.2.3/example/artifacts/no-attention/2/2x6.pdf +0 -0
  47. pmecg-0.2.3/example/artifacts/no-attention/2/2x6.png +0 -0
  48. pmecg-0.2.3/example/artifacts/no-attention/2/4x3.pdf +0 -0
  49. pmecg-0.2.3/example/artifacts/no-attention/2/4x3.png +0 -0
  50. pmecg-0.2.3/example/artifacts/no-attention/3/1x3.pdf +0 -0
  51. pmecg-0.2.3/example/artifacts/no-attention/3/1x3.png +0 -0
  52. pmecg-0.2.3/example/artifacts/no-attention/3/2x6.pdf +0 -0
  53. pmecg-0.2.3/example/artifacts/no-attention/3/2x6.png +0 -0
  54. pmecg-0.2.3/example/artifacts/no-attention/3/4x3.pdf +0 -0
  55. pmecg-0.2.3/example/artifacts/no-attention/3/4x3.png +0 -0
  56. pmecg-0.2.3/example/artifacts/no-attention/4/1x3.pdf +0 -0
  57. pmecg-0.2.3/example/artifacts/no-attention/4/1x3.png +0 -0
  58. pmecg-0.2.3/example/artifacts/no-attention/4/2x6.pdf +0 -0
  59. pmecg-0.2.3/example/artifacts/no-attention/4/2x6.png +0 -0
  60. pmecg-0.2.3/example/artifacts/no-attention/4/4x3.pdf +0 -0
  61. pmecg-0.2.3/example/artifacts/no-attention/4/4x3.png +0 -0
  62. pmecg-0.2.3/example/artifacts/no-attention/5/1x3.pdf +649 -0
  63. pmecg-0.2.3/example/artifacts/no-attention/5/1x3.png +0 -0
  64. pmecg-0.2.3/example/artifacts/no-attention/5/2x6.pdf +0 -0
  65. pmecg-0.2.3/example/artifacts/no-attention/5/2x6.png +0 -0
  66. pmecg-0.2.3/example/artifacts/no-attention/5/4x3.pdf +0 -0
  67. pmecg-0.2.3/example/artifacts/no-attention/5/4x3.png +0 -0
  68. pmecg-0.2.3/pixi.lock +16652 -0
  69. pmecg-0.2.3/pyproject.toml +150 -0
  70. pmecg-0.2.3/src/pmecg/__init__.py +37 -0
  71. pmecg-0.2.3/src/pmecg/plot.py +387 -0
  72. pmecg-0.2.3/src/pmecg/py.typed +0 -0
  73. pmecg-0.2.3/src/pmecg/types.py +79 -0
  74. pmecg-0.2.3/src/pmecg/utils/__init__.py +0 -0
  75. pmecg-0.2.3/src/pmecg/utils/attention.py +805 -0
  76. pmecg-0.2.3/src/pmecg/utils/data.py +421 -0
  77. pmecg-0.2.3/src/pmecg/utils/plot.py +563 -0
  78. pmecg-0.2.3/tests/output_helpers.py +36 -0
  79. pmecg-0.2.3/tests/ptbxl_helper.py +98 -0
  80. pmecg-0.2.3/tests/test_attention.py +1156 -0
  81. pmecg-0.2.3/tests/test_attention_artifacts.py +170 -0
  82. pmecg-0.2.3/tests/test_data.py +442 -0
  83. pmecg-0.2.3/tests/test_plot_systematic.py +919 -0
  84. pmecg-0.2.3/tests/test_ptbxl_artifacts.py +64 -0
@@ -0,0 +1,63 @@
1
+ # pmecg — Copilot Instructions
2
+
3
+ `pmecg` is a Python library for plotting high-quality, paper-like ECG signals using Matplotlib.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ pixi install # Install the default development environment
9
+ pixi run test # Run all tests in the default environment
10
+ pixi run test-fast # Unit + structural tests only (no network)
11
+ pixi run pytest tests/test_data.py::TestSegmentLeads -v # Run a specific test class
12
+ pixi run pytest tests/test_data.py::TestNumpyToDataframe::test_shape -v # Run a single test
13
+ pixi run lint # Lint + formatting check
14
+ pixi run ruff check . --fix # Lint with auto-fix
15
+ ```
16
+
17
+ Always use `pixi run` (or named Pixi tasks) to invoke Python tools — never `python` or `pytest` directly.
18
+
19
+ Integration tests (`@pytest.mark.integration`) require network access to download the PTB-XL dataset and are slow; skip them with `-m "not integration"` for routine development.
20
+
21
+ ## Architecture
22
+
23
+ The codebase has three layers:
24
+
25
+ 1. **Public API** (`src/pmecg/plot.py`, re-exported via `src/pmecg/__init__.py`):
26
+ - `ECGPlotter` — main class; instantiate with visual parameters, then call `.plot()`
27
+ - `ECGStats`, `ECGInformation` — dataclasses for optional metadata overlays
28
+ - `template_factory()`, `LeadsMap`, `SUPPORTED_LEADS` — configuration helpers
29
+
30
+ 2. **Data layer** (`src/pmecg/utils/data.py`):
31
+ - Normalizes ECG input (numpy arrays, lists of arrays, or DataFrames) into a DataFrame
32
+ - Resolves and validates configuration (template expansion, lead name mapping)
33
+ - Segments leads into rows for rendering
34
+
35
+ 3. **Rendering layer** (`src/pmecg/utils/plot.py`):
36
+ - Low-level Matplotlib logic: grid drawing, figure sizing, calibration pulse, row plotting
37
+ - Uses `MM_PER_INCH = 25.4` and physical-unit constants (mm) for exact paper sizes
38
+
39
+ Data flows: `ECGPlotter.plot()` → data layer normalizes input and expands config → rendering layer produces a Matplotlib `Figure`.
40
+
41
+ ## Key Conventions
42
+
43
+ **Configuration system:**
44
+ - A *configuration* is `list[list[str] | str]` — each element is a row; a string is a full-width lead, a sublist is concatenated leads sharing a row.
45
+ - Built-in templates (`"4x3"`, `"2x6"`, `"1x12"`, etc.) must be expanded via `template_factory()` before passing to `ECGPlotter.plot()` — `plot()` does not accept raw template strings.
46
+ - Custom lead names are mapped to canonical names (`"I"`, `"II"`, ..., `"V6"`) via `LeadsMap`.
47
+
48
+ **Types:**
49
+ ```python
50
+ ECGDataType = tuple[list[np.ndarray] | np.ndarray, list[str]] | pd.DataFrame
51
+ ConfigurationDataType = list[list[str] | str]
52
+ ```
53
+
54
+ **Naming:**
55
+ - Public symbols: `PascalCase` (`ECGPlotter`, `LeadsMap`)
56
+ - Internal helpers: leading underscore (`_segment_leads`, `_plot_row`, `_RenderContext`)
57
+ - Module-level constants: `UPPER_CASE` (`MM_PER_INCH`, `SUPPORTED_LEADS`, `CAL_PULSE_AMP_MV`)
58
+
59
+ **Typing:** Full type annotations are required throughout (`py.typed` marker is present). Docstrings follow NumPy/SciPy style.
60
+
61
+ **Linting:** Ruff with rules `E, W, F, I, UP, B` and line length 128, targeting Python 3.8+. The `pixi run lint` task runs `ruff check . && ruff format --check .`.
62
+
63
+ **Tests:** `test_plot_systematic.py` uses `matplotlib.use("Agg")` for headless rendering. Tests are heavily parametrized across all built-in templates. New layout or data-handling changes should be validated against the systematic tests.
@@ -0,0 +1,95 @@
1
+ name: Run tests and linting
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [ main ]
6
+ paths:
7
+ - '**.py'
8
+ - 'pyproject.toml'
9
+ - 'pixi.lock'
10
+ - '.github/workflows/ci.yml'
11
+ push:
12
+ branches: [ main ]
13
+ paths:
14
+ - '**.py'
15
+ - 'pyproject.toml'
16
+ - 'pixi.lock'
17
+ - '.github/workflows/ci.yml'
18
+
19
+ jobs:
20
+ setup:
21
+ runs-on: ubuntu-latest
22
+
23
+ steps:
24
+ - name: Checkout code
25
+ uses: actions/checkout@v4
26
+
27
+ - name: Install all pixi environments
28
+ uses: prefix-dev/setup-pixi@v0.9.4
29
+ with:
30
+ environments: default py38 py39 py310 py311 py312 py313 py314
31
+ locked: true
32
+ cache: true
33
+ cache-write: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
34
+
35
+ lint:
36
+ runs-on: ubuntu-latest
37
+ needs: setup
38
+
39
+ steps:
40
+ - name: Checkout code
41
+ uses: actions/checkout@v4
42
+
43
+ - name: Restore pixi environments
44
+ uses: prefix-dev/setup-pixi@v0.9.4
45
+ with:
46
+ environments: default
47
+ locked: true
48
+ cache: true
49
+ cache-write: false
50
+
51
+ - name: Lint and format check
52
+ run: pixi run lint
53
+
54
+ test:
55
+ runs-on: ubuntu-latest
56
+ needs: setup
57
+ strategy:
58
+ matrix:
59
+ include:
60
+ - python-version: '3.8'
61
+ environment: py38
62
+ - python-version: '3.9'
63
+ environment: py39
64
+ - python-version: '3.10'
65
+ environment: py310
66
+ - python-version: '3.11'
67
+ environment: py311
68
+ - python-version: '3.12'
69
+ environment: py312
70
+ - python-version: '3.13'
71
+ environment: py313
72
+ - python-version: '3.14'
73
+ environment: py314
74
+ fail-fast: false
75
+
76
+ steps:
77
+ - name: Checkout code
78
+ uses: actions/checkout@v4
79
+
80
+ - name: Restore pixi environment
81
+ uses: prefix-dev/setup-pixi@v0.9.4
82
+ with:
83
+ environments: ${{ matrix.environment }}
84
+ locked: true
85
+ cache: true
86
+ cache-write: false
87
+
88
+ - name: Cache PTB-XL data
89
+ uses: actions/cache@v4
90
+ with:
91
+ path: tests/.ptbxl-cache
92
+ key: ptbxl-${{ hashFiles('tests/ptbxl_helper.py') }}
93
+
94
+ - name: Run tests on Python ${{ matrix.python-version }}
95
+ run: pixi run -e ${{ matrix.environment }} test
@@ -0,0 +1,95 @@
1
+ name: Release pmecg
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Build wheel
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ persist-credentials: false
17
+
18
+ - name: Set up pixi
19
+ uses: prefix-dev/setup-pixi@v0.9.4
20
+ with:
21
+ environments: default
22
+ locked: false
23
+ cache: true
24
+
25
+ - name: Build wheel and source tarball
26
+ run: pixi run build-dist
27
+
28
+ - name: Store distribution packages
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: python-package-distributions
32
+ path: dist/
33
+
34
+ github-release:
35
+ name: Publish to GitHub Release
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+
39
+ permissions:
40
+ contents: write
41
+
42
+ steps:
43
+ - name: Download distributions
44
+ uses: actions/download-artifact@v4
45
+ with:
46
+ name: python-package-distributions
47
+ path: dist/
48
+
49
+ - name: Create GitHub Release and upload assets
50
+ env:
51
+ GITHUB_TOKEN: ${{ github.token }}
52
+ run: |
53
+ TAG="${GITHUB_REF#refs/tags/}"
54
+ gh release create "$TAG" dist/** \
55
+ --repo "$GITHUB_REPOSITORY" \
56
+ --title "pmecg $TAG" \
57
+ --generate-notes
58
+
59
+ publish-pypi:
60
+ name: Publish to PyPI
61
+ needs: github-release
62
+ runs-on: ubuntu-latest
63
+
64
+ environment:
65
+ name: pypi
66
+ url: https://pypi.org/p/pmecg
67
+
68
+ permissions:
69
+ id-token: write
70
+
71
+ steps:
72
+ - name: Download distributions
73
+ uses: actions/download-artifact@v4
74
+ with:
75
+ name: python-package-distributions
76
+ path: dist/
77
+
78
+ - name: Publish to PyPI
79
+ uses: pypa/gh-action-pypi-publish@release/v1
80
+
81
+ trigger-rtd:
82
+ name: Trigger Read the Docs build
83
+ needs: [github-release, publish-pypi]
84
+ runs-on: ubuntu-latest
85
+
86
+ steps:
87
+ - name: Trigger RTD webhook
88
+ run: |
89
+ PAYLOAD="{\"ref\":\"${GITHUB_REF}\"}"
90
+ SIG=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.RTD_SECRET }}" | awk '{print $2}')
91
+ curl -X POST \
92
+ -H "X-Hub-Signature-256: sha256=$SIG" \
93
+ -H "Content-Type: application/json" \
94
+ -d "$PAYLOAD" \
95
+ https://app.readthedocs.org/api/v2/webhook/pmecg/322018/
pmecg-0.2.3/.gitignore ADDED
@@ -0,0 +1,40 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Gemini repo
13
+ .gemini.md
14
+
15
+ # CLAUDE
16
+ CLAUDE.md
17
+
18
+ # uv
19
+ uv.lock
20
+
21
+ # IDE
22
+ .vscode/
23
+ .idea/
24
+ *.swp
25
+ *.swo
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+
31
+ # Test / coverage
32
+ .pytest_cache/
33
+ .coverage
34
+ htmlcov/
35
+ simple_plot.py
36
+ example_ecg.mat
37
+ tests/.ptbxl-cache/
38
+
39
+ # Docs builds
40
+ _build/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,14 @@
1
+ # Read the Docs configuration file
2
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3
+
4
+ # Required
5
+ version: 2
6
+
7
+ build:
8
+ os: ubuntu-24.04
9
+ tools:
10
+ python: "3.13"
11
+ commands:
12
+ - curl -fsSL https://pixi.sh/install.sh | bash
13
+ - $HOME/.pixi/bin/pixi run build-docs
14
+ - mkdir -p $READTHEDOCS_OUTPUT/html && cp -r docs/_build/html/. $READTHEDOCS_OUTPUT/html/