pylocuszoom 0.3.0__tar.gz → 0.5.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.
- pylocuszoom-0.5.0/.gitattributes +3 -0
- pylocuszoom-0.5.0/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- pylocuszoom-0.5.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- pylocuszoom-0.5.0/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/.github/workflows/ci.yml +1 -1
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/.gitignore +3 -0
- pylocuszoom-0.5.0/.pre-commit-config.yaml +7 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/CHANGELOG.md +41 -1
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/PKG-INFO +104 -24
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/README.md +101 -22
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/docs/ARCHITECTURE.md +30 -8
- pylocuszoom-0.5.0/docs/USER_GUIDE.md +740 -0
- pylocuszoom-0.5.0/examples/eqtl_bokeh.html +61 -0
- pylocuszoom-0.5.0/examples/eqtl_overlay.png +0 -0
- pylocuszoom-0.5.0/examples/eqtl_plotly.html +3888 -0
- pylocuszoom-0.5.0/examples/finemapping_bokeh.html +61 -0
- pylocuszoom-0.5.0/examples/finemapping_plot.png +0 -0
- pylocuszoom-0.5.0/examples/finemapping_plotly.html +3888 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/examples/generate_readme_plots.py +78 -2
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/examples/getting_started.ipynb +3 -79
- pylocuszoom-0.5.0/examples/regional_plot.png +0 -0
- pylocuszoom-0.5.0/examples/stacked_plot.png +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/pyproject.toml +3 -2
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/__init__.py +52 -1
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/base.py +45 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/bokeh_backend.py +138 -48
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/matplotlib_backend.py +104 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/plotly_backend.py +212 -64
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/colors.py +3 -1
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/gene_track.py +1 -0
- pylocuszoom-0.5.0/src/pylocuszoom/loaders.py +862 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/plotter.py +84 -113
- pylocuszoom-0.5.0/src/pylocuszoom/py.typed +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/recombination.py +4 -4
- pylocuszoom-0.5.0/src/pylocuszoom/schemas.py +395 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_finemapping.py +6 -2
- pylocuszoom-0.5.0/tests/test_loaders.py +466 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_notebook_backends.py +342 -1
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_plotter.py +236 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/uv.lock +157 -1
- pylocuszoom-0.3.0/examples/eqtl_overlay.png +0 -0
- pylocuszoom-0.3.0/examples/finemapping_plot.png +0 -0
- pylocuszoom-0.3.0/examples/regional_plot.png +0 -0
- pylocuszoom-0.3.0/examples/stacked_plot.png +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/.github/workflows/publish.yml +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/CONTRIBUTING.md +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/LICENSE.md +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/bioconda/meta.yaml +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/logo.svg +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/__init__.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/eqtl.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/finemapping.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/labels.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/ld.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/logging.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/reference_data/__init__.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/utils.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/conftest.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_colors.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_gene_track.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_labels.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_ld.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_logging.py +0 -0
- {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_recombination.py +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug Report
|
|
3
|
+
about: Report a bug or unexpected behavior
|
|
4
|
+
title: ''
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
|
|
11
|
+
A clear description of the bug.
|
|
12
|
+
|
|
13
|
+
## Minimal Reproducible Example
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from pylocuszoom import LocusZoomPlotter
|
|
17
|
+
import pandas as pd
|
|
18
|
+
|
|
19
|
+
# Minimal code to reproduce the issue
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Expected Behavior
|
|
23
|
+
|
|
24
|
+
What you expected to happen.
|
|
25
|
+
|
|
26
|
+
## Actual Behavior
|
|
27
|
+
|
|
28
|
+
What actually happened. Include error messages if applicable.
|
|
29
|
+
|
|
30
|
+
## Environment
|
|
31
|
+
|
|
32
|
+
- pylocuszoom version:
|
|
33
|
+
- Python version:
|
|
34
|
+
- OS:
|
|
35
|
+
- Installation method: pip / conda / uv
|
|
36
|
+
|
|
37
|
+
## Additional Context
|
|
38
|
+
|
|
39
|
+
Any other relevant information (screenshots, data samples, etc.)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature Request
|
|
3
|
+
about: Suggest a new feature or enhancement
|
|
4
|
+
title: ''
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
What problem does this feature solve?
|
|
12
|
+
|
|
13
|
+
## Proposed Solution
|
|
14
|
+
|
|
15
|
+
Describe the feature you'd like.
|
|
16
|
+
|
|
17
|
+
## Alternatives Considered
|
|
18
|
+
|
|
19
|
+
Any alternative solutions or workarounds you've tried.
|
|
20
|
+
|
|
21
|
+
## Additional Context
|
|
22
|
+
|
|
23
|
+
Any other relevant information (mockups, examples from other tools, etc.)
|
|
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.5.0] - 2026-01-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Hover tooltips for fine-mapping scatter plots (Plotly/Bokeh backends)
|
|
14
|
+
- Hover tooltips for eQTL scatter plots (Plotly/Bokeh backends)
|
|
15
|
+
- Interactive HTML example plots for eQTL and fine-mapping (Plotly/Bokeh)
|
|
16
|
+
- Comprehensive marker and hover data tests for interactive backends
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Plotly/Bokeh backends now hide grid lines for cleaner LocusZoom appearance
|
|
20
|
+
- Plotly/Bokeh backends now show black axis lines (matching matplotlib style)
|
|
21
|
+
- Plotly/Bokeh gene track panels now hide y-axis (ticks, labels, line, grid)
|
|
22
|
+
- Plotly/Bokeh backends now hide minor ticks and zero lines
|
|
23
|
+
|
|
24
|
+
## [0.4.0] - 2026-01-26
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **File format loaders** for common GWAS, eQTL, and fine-mapping formats:
|
|
28
|
+
- GWAS: `load_gwas`, `load_plink_assoc`, `load_regenie`, `load_bolt_lmm`, `load_gemma`, `load_saige`, `load_gwas_catalog`
|
|
29
|
+
- eQTL: `load_gtex_eqtl`, `load_eqtl_catalogue`, `load_matrixeqtl`
|
|
30
|
+
- Fine-mapping: `load_susie`, `load_finemap`, `load_caviar`, `load_polyfun`
|
|
31
|
+
- Gene annotations: `load_gtf`, `load_bed`, `load_ensembl_genes`
|
|
32
|
+
- Pydantic validation for file loaders with detailed error messages
|
|
33
|
+
- `py.typed` marker for PEP 561 type checking support
|
|
34
|
+
- Pre-commit configuration for automated linting
|
|
35
|
+
- GitHub issue templates for bug reports and feature requests
|
|
36
|
+
- Codecov badge in README
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- eQTL and fine-mapping legends now route through backend protocol (works with all backends)
|
|
40
|
+
- Simplified backend code with reduced duplication
|
|
41
|
+
- Backend protocol class diagram added to ARCHITECTURE.md
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
- Additional robustness improvements for edge cases
|
|
45
|
+
|
|
8
46
|
## [0.3.0] - 2026-01-26
|
|
9
47
|
|
|
10
48
|
### Added
|
|
@@ -80,7 +118,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
80
118
|
- bokeh >= 3.8.2
|
|
81
119
|
- kaleido >= 0.2.0
|
|
82
120
|
|
|
83
|
-
[Unreleased]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.
|
|
121
|
+
[Unreleased]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.5.0...HEAD
|
|
122
|
+
[0.5.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.4.0...v0.5.0
|
|
123
|
+
[0.4.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.3.0...v0.4.0
|
|
84
124
|
[0.3.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.2.0...v0.3.0
|
|
85
125
|
[0.2.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.1.0...v0.2.0
|
|
86
126
|
[0.1.0]: https://github.com/michael-denyer/pyLocusZoom/releases/tag/v0.1.0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pylocuszoom
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Publication-ready regional association plots with LD coloring, gene tracks, and recombination overlays
|
|
5
5
|
Project-URL: Homepage, https://github.com/michael-denyer/pylocuszoom
|
|
6
6
|
Project-URL: Documentation, https://github.com/michael-denyer/pylocuszoom#readme
|
|
7
7
|
Project-URL: Repository, https://github.com/michael-denyer/pylocuszoom
|
|
@@ -27,6 +27,7 @@ Requires-Dist: matplotlib>=3.5.0
|
|
|
27
27
|
Requires-Dist: numpy>=1.21.0
|
|
28
28
|
Requires-Dist: pandas>=1.4.0
|
|
29
29
|
Requires-Dist: plotly>=5.0.0
|
|
30
|
+
Requires-Dist: pydantic>=2.0.0
|
|
30
31
|
Requires-Dist: pyliftover>=0.4
|
|
31
32
|
Provides-Extra: all
|
|
32
33
|
Requires-Dist: pyspark>=3.0.0; extra == 'all'
|
|
@@ -38,39 +39,44 @@ Provides-Extra: spark
|
|
|
38
39
|
Requires-Dist: pyspark>=3.0.0; extra == 'spark'
|
|
39
40
|
Description-Content-Type: text/markdown
|
|
40
41
|
|
|
41
|
-
# pyLocusZoom
|
|
42
|
-
|
|
43
42
|
[](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml)
|
|
44
|
-
[](https://codecov.io/gh/michael-denyer/pyLocusZoom)
|
|
44
|
+
[](https://pypi.org/project/pylocuszoom/)
|
|
45
|
+
[](https://anaconda.org/bioconda/pylocuszoom)
|
|
46
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
45
47
|
[](https://www.python.org/downloads/)
|
|
46
48
|
[](https://github.com/astral-sh/ruff)
|
|
47
|
-
|
|
48
49
|
[](https://matplotlib.org/)
|
|
49
50
|
[](https://plotly.com/python/)
|
|
50
51
|
[](https://bokeh.org/)
|
|
51
52
|
[](https://pandas.pydata.org/)
|
|
52
|
-
|
|
53
53
|
<img src="logo.svg" alt="pyLocusZoom logo" width="120" align="right">
|
|
54
|
+
# pyLocusZoom
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
Publication-ready regional association plots with LD coloring, gene tracks, and recombination overlays.
|
|
56
57
|
|
|
57
58
|
Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.com/myles-lewis/locuszoomr).
|
|
58
59
|
|
|
59
60
|
## Features
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- **
|
|
64
|
-
- **
|
|
65
|
-
- **
|
|
66
|
-
- **
|
|
67
|
-
- **
|
|
68
|
-
- **
|
|
69
|
-
- **eQTL overlay**: Expression QTL data as separate panel
|
|
70
|
-
- **PySpark support**: Handles large-scale genomics DataFrames
|
|
62
|
+
1. **Regional association plot**:
|
|
63
|
+
|
|
64
|
+
- **Multi-species support**: Built-in reference data for *Canis lupus familiaris* (CanFam3.1/CanFam4) and *Felis catus* (FelCat9), or optionally provide your own for any species
|
|
65
|
+
- **LD coloring**: SNPs colored by linkage disequilibrium (R²) with lead variant
|
|
66
|
+
- **Gene tracks**: Annotated gene/exon positions below the association plot
|
|
67
|
+
- **Recombination rate**: Overlay showing recombination rate across region (*Canis lupus familiaris* only)
|
|
68
|
+
- **SNP labels (matplotlib)**: Automatic labeling of lead SNPs with RS ID
|
|
69
|
+
- **Tooltips (Bokeh and Plotly)**: Mouseover for detailed SNP data
|
|
71
70
|
|
|
72
71
|

|
|
73
72
|
|
|
73
|
+
2. **Stacked plots**: Compare multiple GWAS/phenotypes vertically
|
|
74
|
+
3. **eQTL plot**: Expression QTL data aligned with association plots and gene tracks
|
|
75
|
+
4. **Fine-mapping plots**: Visualize SuSiE credible sets with posterior inclusion probabilities
|
|
76
|
+
5. **Multiple charting libraries**: matplotlib (static), plotly (interactive), bokeh (dashboards)
|
|
77
|
+
6. **Pandas and PySpark support**: Works with both Pandas and PySpark DataFrames for large-scale genomics data
|
|
78
|
+
7. **Convenience data file loaders**: Load and validate common GWAS, eQTL and fine-mapping file formats
|
|
79
|
+
|
|
74
80
|
## Installation
|
|
75
81
|
|
|
76
82
|
```bash
|
|
@@ -186,13 +192,13 @@ fig.write_html("plot.html")
|
|
|
186
192
|
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="bokeh")
|
|
187
193
|
```
|
|
188
194
|
|
|
189
|
-
| Backend | Output | Best For |
|
|
190
|
-
|
|
191
|
-
| `matplotlib` | Static PNG/PDF/SVG | Publications, presentations |
|
|
192
|
-
| `plotly` | Interactive HTML | Web reports, data exploration |
|
|
193
|
-
| `bokeh` | Interactive HTML | Dashboards, web apps |
|
|
195
|
+
| Backend | Output | Best For | Features |
|
|
196
|
+
|---------|--------|----------|----------|
|
|
197
|
+
| `matplotlib` | Static PNG/PDF/SVG | Publications, presentations | Full feature set with SNP labels |
|
|
198
|
+
| `plotly` | Interactive HTML | Web reports, data exploration | Hover tooltips, pan/zoom |
|
|
199
|
+
| `bokeh` | Interactive HTML | Dashboards, web apps | Hover tooltips, pan/zoom |
|
|
194
200
|
|
|
195
|
-
> **Note:** All backends support gene
|
|
201
|
+
> **Note:** All backends support scatter plots, gene tracks, recombination overlay, and LD legend. SNP labels (auto-positioned with adjustText) are matplotlib-only; interactive backends use hover tooltips instead.
|
|
196
202
|
|
|
197
203
|
## Stacked Plots
|
|
198
204
|
|
|
@@ -209,6 +215,8 @@ fig = plotter.plot_stacked(
|
|
|
209
215
|
)
|
|
210
216
|
```
|
|
211
217
|
|
|
218
|
+

|
|
219
|
+
|
|
212
220
|
## eQTL Overlay
|
|
213
221
|
|
|
214
222
|
Add expression QTL data as a separate panel:
|
|
@@ -229,6 +237,30 @@ fig = plotter.plot_stacked(
|
|
|
229
237
|
)
|
|
230
238
|
```
|
|
231
239
|
|
|
240
|
+

|
|
241
|
+
|
|
242
|
+
## Fine-mapping Visualization
|
|
243
|
+
|
|
244
|
+
Visualize SuSiE or other fine-mapping results with credible set coloring:
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
finemapping_df = pd.DataFrame({
|
|
248
|
+
"pos": [1000500, 1001200, 1002000, 1003500],
|
|
249
|
+
"pip": [0.85, 0.12, 0.02, 0.45], # Posterior inclusion probability
|
|
250
|
+
"cs": [1, 1, 0, 2], # Credible set assignment (0 = not in CS)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
fig = plotter.plot_stacked(
|
|
254
|
+
[gwas_df],
|
|
255
|
+
chrom=1, start=1000000, end=2000000,
|
|
256
|
+
finemapping_df=finemapping_df,
|
|
257
|
+
finemapping_cs_col="cs",
|
|
258
|
+
genes_df=genes_df,
|
|
259
|
+
)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+

|
|
263
|
+
|
|
232
264
|
## PySpark Support
|
|
233
265
|
|
|
234
266
|
For large-scale genomics data, pass PySpark DataFrames directly:
|
|
@@ -245,6 +277,47 @@ pandas_df = to_pandas(spark_gwas_df, sample_size=100000)
|
|
|
245
277
|
|
|
246
278
|
Install PySpark support: `uv add pylocuszoom[spark]`
|
|
247
279
|
|
|
280
|
+
## Loading Data from Files
|
|
281
|
+
|
|
282
|
+
pyLocusZoom includes loaders for common GWAS, eQTL, and fine-mapping file formats:
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
from pylocuszoom import (
|
|
286
|
+
# GWAS loaders
|
|
287
|
+
load_gwas, # Auto-detect format
|
|
288
|
+
load_plink_assoc, # PLINK .assoc, .assoc.linear, .qassoc
|
|
289
|
+
load_regenie, # REGENIE .regenie
|
|
290
|
+
load_bolt_lmm, # BOLT-LMM .stats
|
|
291
|
+
load_gemma, # GEMMA .assoc.txt
|
|
292
|
+
load_saige, # SAIGE output
|
|
293
|
+
# eQTL loaders
|
|
294
|
+
load_gtex_eqtl, # GTEx significant pairs
|
|
295
|
+
load_eqtl_catalogue, # eQTL Catalogue format
|
|
296
|
+
# Fine-mapping loaders
|
|
297
|
+
load_susie, # SuSiE output
|
|
298
|
+
load_finemap, # FINEMAP .snp output
|
|
299
|
+
# Gene annotations
|
|
300
|
+
load_gtf, # GTF/GFF3 files
|
|
301
|
+
load_bed, # BED files
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Auto-detect GWAS format from filename
|
|
305
|
+
gwas_df = load_gwas("results.assoc.linear")
|
|
306
|
+
|
|
307
|
+
# Or use specific loader
|
|
308
|
+
gwas_df = load_regenie("ukb_results.regenie")
|
|
309
|
+
|
|
310
|
+
# Load gene annotations
|
|
311
|
+
genes_df = load_gtf("genes.gtf", feature_type="gene")
|
|
312
|
+
exons_df = load_gtf("genes.gtf", feature_type="exon")
|
|
313
|
+
|
|
314
|
+
# Load eQTL data
|
|
315
|
+
eqtl_df = load_gtex_eqtl("GTEx.signif_pairs.txt.gz", gene="BRCA1")
|
|
316
|
+
|
|
317
|
+
# Load fine-mapping results
|
|
318
|
+
fm_df = load_susie("susie_output.tsv")
|
|
319
|
+
```
|
|
320
|
+
|
|
248
321
|
## Data Formats
|
|
249
322
|
|
|
250
323
|
### GWAS Results DataFrame
|
|
@@ -371,6 +444,13 @@ plotter = LocusZoomPlotter(log_level="DEBUG")
|
|
|
371
444
|
Optional:
|
|
372
445
|
- pyspark >= 3.0.0 (for PySpark DataFrame support) - `uv add pylocuszoom[spark]`
|
|
373
446
|
|
|
447
|
+
## Documentation
|
|
448
|
+
|
|
449
|
+
- [User Guide](docs/USER_GUIDE.md) - Comprehensive documentation with API reference
|
|
450
|
+
- [Architecture](docs/ARCHITECTURE.md) - Design decisions and component overview
|
|
451
|
+
- [Example Notebook](examples/getting_started.ipynb) - Interactive tutorial
|
|
452
|
+
- [CHANGELOG](CHANGELOG.md) - Version history
|
|
453
|
+
|
|
374
454
|
## License
|
|
375
455
|
|
|
376
456
|
GPL-3.0-or-later
|
|
@@ -1,36 +1,41 @@
|
|
|
1
|
-
# pyLocusZoom
|
|
2
|
-
|
|
3
1
|
[](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml)
|
|
4
|
-
[](https://codecov.io/gh/michael-denyer/pyLocusZoom)
|
|
3
|
+
[](https://pypi.org/project/pylocuszoom/)
|
|
4
|
+
[](https://anaconda.org/bioconda/pylocuszoom)
|
|
5
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
5
6
|
[](https://www.python.org/downloads/)
|
|
6
7
|
[](https://github.com/astral-sh/ruff)
|
|
7
|
-
|
|
8
8
|
[](https://matplotlib.org/)
|
|
9
9
|
[](https://plotly.com/python/)
|
|
10
10
|
[](https://bokeh.org/)
|
|
11
11
|
[](https://pandas.pydata.org/)
|
|
12
|
-
|
|
13
12
|
<img src="logo.svg" alt="pyLocusZoom logo" width="120" align="right">
|
|
13
|
+
# pyLocusZoom
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Publication-ready regional association plots with LD coloring, gene tracks, and recombination overlays.
|
|
16
16
|
|
|
17
17
|
Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.com/myles-lewis/locuszoomr).
|
|
18
18
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **eQTL overlay**: Expression QTL data as separate panel
|
|
30
|
-
- **PySpark support**: Handles large-scale genomics DataFrames
|
|
21
|
+
1. **Regional association plot**:
|
|
22
|
+
|
|
23
|
+
- **Multi-species support**: Built-in reference data for *Canis lupus familiaris* (CanFam3.1/CanFam4) and *Felis catus* (FelCat9), or optionally provide your own for any species
|
|
24
|
+
- **LD coloring**: SNPs colored by linkage disequilibrium (R²) with lead variant
|
|
25
|
+
- **Gene tracks**: Annotated gene/exon positions below the association plot
|
|
26
|
+
- **Recombination rate**: Overlay showing recombination rate across region (*Canis lupus familiaris* only)
|
|
27
|
+
- **SNP labels (matplotlib)**: Automatic labeling of lead SNPs with RS ID
|
|
28
|
+
- **Tooltips (Bokeh and Plotly)**: Mouseover for detailed SNP data
|
|
31
29
|
|
|
32
30
|

|
|
33
31
|
|
|
32
|
+
2. **Stacked plots**: Compare multiple GWAS/phenotypes vertically
|
|
33
|
+
3. **eQTL plot**: Expression QTL data aligned with association plots and gene tracks
|
|
34
|
+
4. **Fine-mapping plots**: Visualize SuSiE credible sets with posterior inclusion probabilities
|
|
35
|
+
5. **Multiple charting libraries**: matplotlib (static), plotly (interactive), bokeh (dashboards)
|
|
36
|
+
6. **Pandas and PySpark support**: Works with both Pandas and PySpark DataFrames for large-scale genomics data
|
|
37
|
+
7. **Convenience data file loaders**: Load and validate common GWAS, eQTL and fine-mapping file formats
|
|
38
|
+
|
|
34
39
|
## Installation
|
|
35
40
|
|
|
36
41
|
```bash
|
|
@@ -146,13 +151,13 @@ fig.write_html("plot.html")
|
|
|
146
151
|
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="bokeh")
|
|
147
152
|
```
|
|
148
153
|
|
|
149
|
-
| Backend | Output | Best For |
|
|
150
|
-
|
|
151
|
-
| `matplotlib` | Static PNG/PDF/SVG | Publications, presentations |
|
|
152
|
-
| `plotly` | Interactive HTML | Web reports, data exploration |
|
|
153
|
-
| `bokeh` | Interactive HTML | Dashboards, web apps |
|
|
154
|
+
| Backend | Output | Best For | Features |
|
|
155
|
+
|---------|--------|----------|----------|
|
|
156
|
+
| `matplotlib` | Static PNG/PDF/SVG | Publications, presentations | Full feature set with SNP labels |
|
|
157
|
+
| `plotly` | Interactive HTML | Web reports, data exploration | Hover tooltips, pan/zoom |
|
|
158
|
+
| `bokeh` | Interactive HTML | Dashboards, web apps | Hover tooltips, pan/zoom |
|
|
154
159
|
|
|
155
|
-
> **Note:** All backends support gene
|
|
160
|
+
> **Note:** All backends support scatter plots, gene tracks, recombination overlay, and LD legend. SNP labels (auto-positioned with adjustText) are matplotlib-only; interactive backends use hover tooltips instead.
|
|
156
161
|
|
|
157
162
|
## Stacked Plots
|
|
158
163
|
|
|
@@ -169,6 +174,8 @@ fig = plotter.plot_stacked(
|
|
|
169
174
|
)
|
|
170
175
|
```
|
|
171
176
|
|
|
177
|
+

|
|
178
|
+
|
|
172
179
|
## eQTL Overlay
|
|
173
180
|
|
|
174
181
|
Add expression QTL data as a separate panel:
|
|
@@ -189,6 +196,30 @@ fig = plotter.plot_stacked(
|
|
|
189
196
|
)
|
|
190
197
|
```
|
|
191
198
|
|
|
199
|
+

|
|
200
|
+
|
|
201
|
+
## Fine-mapping Visualization
|
|
202
|
+
|
|
203
|
+
Visualize SuSiE or other fine-mapping results with credible set coloring:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
finemapping_df = pd.DataFrame({
|
|
207
|
+
"pos": [1000500, 1001200, 1002000, 1003500],
|
|
208
|
+
"pip": [0.85, 0.12, 0.02, 0.45], # Posterior inclusion probability
|
|
209
|
+
"cs": [1, 1, 0, 2], # Credible set assignment (0 = not in CS)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
fig = plotter.plot_stacked(
|
|
213
|
+
[gwas_df],
|
|
214
|
+
chrom=1, start=1000000, end=2000000,
|
|
215
|
+
finemapping_df=finemapping_df,
|
|
216
|
+
finemapping_cs_col="cs",
|
|
217
|
+
genes_df=genes_df,
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+

|
|
222
|
+
|
|
192
223
|
## PySpark Support
|
|
193
224
|
|
|
194
225
|
For large-scale genomics data, pass PySpark DataFrames directly:
|
|
@@ -205,6 +236,47 @@ pandas_df = to_pandas(spark_gwas_df, sample_size=100000)
|
|
|
205
236
|
|
|
206
237
|
Install PySpark support: `uv add pylocuszoom[spark]`
|
|
207
238
|
|
|
239
|
+
## Loading Data from Files
|
|
240
|
+
|
|
241
|
+
pyLocusZoom includes loaders for common GWAS, eQTL, and fine-mapping file formats:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from pylocuszoom import (
|
|
245
|
+
# GWAS loaders
|
|
246
|
+
load_gwas, # Auto-detect format
|
|
247
|
+
load_plink_assoc, # PLINK .assoc, .assoc.linear, .qassoc
|
|
248
|
+
load_regenie, # REGENIE .regenie
|
|
249
|
+
load_bolt_lmm, # BOLT-LMM .stats
|
|
250
|
+
load_gemma, # GEMMA .assoc.txt
|
|
251
|
+
load_saige, # SAIGE output
|
|
252
|
+
# eQTL loaders
|
|
253
|
+
load_gtex_eqtl, # GTEx significant pairs
|
|
254
|
+
load_eqtl_catalogue, # eQTL Catalogue format
|
|
255
|
+
# Fine-mapping loaders
|
|
256
|
+
load_susie, # SuSiE output
|
|
257
|
+
load_finemap, # FINEMAP .snp output
|
|
258
|
+
# Gene annotations
|
|
259
|
+
load_gtf, # GTF/GFF3 files
|
|
260
|
+
load_bed, # BED files
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Auto-detect GWAS format from filename
|
|
264
|
+
gwas_df = load_gwas("results.assoc.linear")
|
|
265
|
+
|
|
266
|
+
# Or use specific loader
|
|
267
|
+
gwas_df = load_regenie("ukb_results.regenie")
|
|
268
|
+
|
|
269
|
+
# Load gene annotations
|
|
270
|
+
genes_df = load_gtf("genes.gtf", feature_type="gene")
|
|
271
|
+
exons_df = load_gtf("genes.gtf", feature_type="exon")
|
|
272
|
+
|
|
273
|
+
# Load eQTL data
|
|
274
|
+
eqtl_df = load_gtex_eqtl("GTEx.signif_pairs.txt.gz", gene="BRCA1")
|
|
275
|
+
|
|
276
|
+
# Load fine-mapping results
|
|
277
|
+
fm_df = load_susie("susie_output.tsv")
|
|
278
|
+
```
|
|
279
|
+
|
|
208
280
|
## Data Formats
|
|
209
281
|
|
|
210
282
|
### GWAS Results DataFrame
|
|
@@ -331,6 +403,13 @@ plotter = LocusZoomPlotter(log_level="DEBUG")
|
|
|
331
403
|
Optional:
|
|
332
404
|
- pyspark >= 3.0.0 (for PySpark DataFrame support) - `uv add pylocuszoom[spark]`
|
|
333
405
|
|
|
406
|
+
## Documentation
|
|
407
|
+
|
|
408
|
+
- [User Guide](docs/USER_GUIDE.md) - Comprehensive documentation with API reference
|
|
409
|
+
- [Architecture](docs/ARCHITECTURE.md) - Design decisions and component overview
|
|
410
|
+
- [Example Notebook](examples/getting_started.ipynb) - Interactive tutorial
|
|
411
|
+
- [CHANGELOG](CHANGELOG.md) - Version history
|
|
412
|
+
|
|
334
413
|
## License
|
|
335
414
|
|
|
336
415
|
GPL-3.0-or-later
|
|
@@ -129,14 +129,36 @@ flowchart LR
|
|
|
129
129
|
|
|
130
130
|
All backends implement the `PlotBackend` protocol defined in `backends/base.py`:
|
|
131
131
|
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
```mermaid
|
|
133
|
+
classDiagram
|
|
134
|
+
class PlotBackend {
|
|
135
|
+
<<Protocol>>
|
|
136
|
+
+create_figure()
|
|
137
|
+
+scatter()
|
|
138
|
+
+line()
|
|
139
|
+
+fill_between()
|
|
140
|
+
+axhline()
|
|
141
|
+
+set_xlabel()
|
|
142
|
+
+set_ylabel()
|
|
143
|
+
+add_ld_legend()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class MatplotlibBackend {
|
|
147
|
+
+fig: Figure
|
|
148
|
+
+ax: Axes
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
class PlotlyBackend {
|
|
152
|
+
+fig: go.Figure
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
class BokehBackend {
|
|
156
|
+
+fig: figure
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
PlotBackend <|.. MatplotlibBackend
|
|
160
|
+
PlotBackend <|.. PlotlyBackend
|
|
161
|
+
PlotBackend <|.. BokehBackend
|
|
140
162
|
```
|
|
141
163
|
|
|
142
164
|
## Module Responsibilities
|