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.
Files changed (64) hide show
  1. pylocuszoom-0.5.0/.gitattributes +3 -0
  2. pylocuszoom-0.5.0/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  3. pylocuszoom-0.5.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. pylocuszoom-0.5.0/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  5. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/.github/workflows/ci.yml +1 -1
  6. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/.gitignore +3 -0
  7. pylocuszoom-0.5.0/.pre-commit-config.yaml +7 -0
  8. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/CHANGELOG.md +41 -1
  9. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/PKG-INFO +104 -24
  10. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/README.md +101 -22
  11. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/docs/ARCHITECTURE.md +30 -8
  12. pylocuszoom-0.5.0/docs/USER_GUIDE.md +740 -0
  13. pylocuszoom-0.5.0/examples/eqtl_bokeh.html +61 -0
  14. pylocuszoom-0.5.0/examples/eqtl_overlay.png +0 -0
  15. pylocuszoom-0.5.0/examples/eqtl_plotly.html +3888 -0
  16. pylocuszoom-0.5.0/examples/finemapping_bokeh.html +61 -0
  17. pylocuszoom-0.5.0/examples/finemapping_plot.png +0 -0
  18. pylocuszoom-0.5.0/examples/finemapping_plotly.html +3888 -0
  19. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/examples/generate_readme_plots.py +78 -2
  20. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/examples/getting_started.ipynb +3 -79
  21. pylocuszoom-0.5.0/examples/regional_plot.png +0 -0
  22. pylocuszoom-0.5.0/examples/stacked_plot.png +0 -0
  23. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/pyproject.toml +3 -2
  24. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/__init__.py +52 -1
  25. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/base.py +45 -0
  26. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/bokeh_backend.py +138 -48
  27. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/matplotlib_backend.py +104 -0
  28. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/plotly_backend.py +212 -64
  29. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/colors.py +3 -1
  30. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/gene_track.py +1 -0
  31. pylocuszoom-0.5.0/src/pylocuszoom/loaders.py +862 -0
  32. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/plotter.py +84 -113
  33. pylocuszoom-0.5.0/src/pylocuszoom/py.typed +0 -0
  34. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/recombination.py +4 -4
  35. pylocuszoom-0.5.0/src/pylocuszoom/schemas.py +395 -0
  36. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_finemapping.py +6 -2
  37. pylocuszoom-0.5.0/tests/test_loaders.py +466 -0
  38. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_notebook_backends.py +342 -1
  39. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_plotter.py +236 -0
  40. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/uv.lock +157 -1
  41. pylocuszoom-0.3.0/examples/eqtl_overlay.png +0 -0
  42. pylocuszoom-0.3.0/examples/finemapping_plot.png +0 -0
  43. pylocuszoom-0.3.0/examples/regional_plot.png +0 -0
  44. pylocuszoom-0.3.0/examples/stacked_plot.png +0 -0
  45. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/.github/workflows/publish.yml +0 -0
  46. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/CONTRIBUTING.md +0 -0
  47. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/LICENSE.md +0 -0
  48. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/bioconda/meta.yaml +0 -0
  49. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/logo.svg +0 -0
  50. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/backends/__init__.py +0 -0
  51. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/eqtl.py +0 -0
  52. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/finemapping.py +0 -0
  53. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/labels.py +0 -0
  54. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/ld.py +0 -0
  55. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/logging.py +0 -0
  56. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/reference_data/__init__.py +0 -0
  57. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/src/pylocuszoom/utils.py +0 -0
  58. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/conftest.py +0 -0
  59. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_colors.py +0 -0
  60. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_gene_track.py +0 -0
  61. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_labels.py +0 -0
  62. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_ld.py +0 -0
  63. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_logging.py +0 -0
  64. {pylocuszoom-0.3.0 → pylocuszoom-0.5.0}/tests/test_recombination.py +0 -0
@@ -0,0 +1,3 @@
1
+
2
+ # Use bd merge for beads JSONL files
3
+ .beads/issues.jsonl merge=beads
@@ -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,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Documentation
4
+ url: https://github.com/michael-denyer/pyLocusZoom/blob/main/docs/USER_GUIDE.md
5
+ about: Check the user guide before opening an issue
@@ -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.)
@@ -29,7 +29,7 @@ jobs:
29
29
  strategy:
30
30
  fail-fast: false
31
31
  matrix:
32
- python-version: ["3.9", "3.10", "3.11", "3.12"]
32
+ python-version: ["3.10", "3.11", "3.12"]
33
33
 
34
34
  steps:
35
35
  - uses: actions/checkout@v4
@@ -21,5 +21,8 @@ htmlcov/
21
21
  *.swp
22
22
  .claude/
23
23
 
24
+ # Issue tracking (local)
25
+ .beads/
26
+
24
27
  # Project instructions (private)
25
28
  CLAUDE.md
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.9.1
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -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.3.0...HEAD
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.3.0
4
- Summary: Regional association plots for GWAS results with LD coloring, gene tracks, and recombination rate overlays
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
  [![CI](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml/badge.svg)](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml)
44
- [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
43
+ [![codecov](https://codecov.io/gh/michael-denyer/pyLocusZoom/graph/badge.svg)](https://codecov.io/gh/michael-denyer/pyLocusZoom)
44
+ [![PyPI](https://img.shields.io/pypi/v/pylocuszoom)](https://pypi.org/project/pylocuszoom/)
45
+ [![Bioconda](https://img.shields.io/conda/vn/bioconda/pylocuszoom)](https://anaconda.org/bioconda/pylocuszoom)
46
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg)](https://www.gnu.org/licenses/gpl-3.0)
45
47
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
46
48
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
47
-
48
49
  [![Matplotlib](https://img.shields.io/badge/Matplotlib-3.5+-11557c.svg)](https://matplotlib.org/)
49
50
  [![Plotly](https://img.shields.io/badge/Plotly-5.0+-3F4F75.svg)](https://plotly.com/python/)
50
51
  [![Bokeh](https://img.shields.io/badge/Bokeh-3.8+-E6526F.svg)](https://bokeh.org/)
51
52
  [![Pandas](https://img.shields.io/badge/Pandas-1.4+-150458.svg)](https://pandas.pydata.org/)
52
-
53
53
  <img src="logo.svg" alt="pyLocusZoom logo" width="120" align="right">
54
+ # pyLocusZoom
54
55
 
55
- Regional association plots for GWAS results with LD coloring, gene tracks, and recombination rate overlays.
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
- - **LD coloring**: SNPs colored by linkage disequilibrium (R²) with lead variant
62
- - **Gene track**: Annotated gene/exon positions below the association plot
63
- - **Recombination rate**: Overlay showing recombination rate across region (*Canis lupus familiaris* only)
64
- - **SNP labels**: Automatic labeling of top SNPs with RS ID or nearest gene
65
- - **Species support**: Built-in *Canis lupus familiaris* (CanFam3.1/CanFam4), *Felis catus* (FelCat9), or custom species
66
- - **CanFam4 support**: Automatic coordinate liftover for recombination maps
67
- - **Multiple backends**: matplotlib (static), plotly (interactive), bokeh (dashboards)
68
- - **Stacked plots**: Compare multiple GWAS/phenotypes vertically
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
  ![Example regional association plot](examples/regional_plot.png)
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 track, recombination overlay, and LD legend. SNP labels (auto-positioned with adjustText) are matplotlib-only.
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
+ ![Example stacked plot](examples/stacked_plot.png)
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
+ ![Example eQTL overlay plot](examples/eqtl_overlay.png)
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
+ ![Example fine-mapping plot](examples/finemapping_plot.png)
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
  [![CI](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml/badge.svg)](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml)
4
- [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
2
+ [![codecov](https://codecov.io/gh/michael-denyer/pyLocusZoom/graph/badge.svg)](https://codecov.io/gh/michael-denyer/pyLocusZoom)
3
+ [![PyPI](https://img.shields.io/pypi/v/pylocuszoom)](https://pypi.org/project/pylocuszoom/)
4
+ [![Bioconda](https://img.shields.io/conda/vn/bioconda/pylocuszoom)](https://anaconda.org/bioconda/pylocuszoom)
5
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg)](https://www.gnu.org/licenses/gpl-3.0)
5
6
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
6
7
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
7
-
8
8
  [![Matplotlib](https://img.shields.io/badge/Matplotlib-3.5+-11557c.svg)](https://matplotlib.org/)
9
9
  [![Plotly](https://img.shields.io/badge/Plotly-5.0+-3F4F75.svg)](https://plotly.com/python/)
10
10
  [![Bokeh](https://img.shields.io/badge/Bokeh-3.8+-E6526F.svg)](https://bokeh.org/)
11
11
  [![Pandas](https://img.shields.io/badge/Pandas-1.4+-150458.svg)](https://pandas.pydata.org/)
12
-
13
12
  <img src="logo.svg" alt="pyLocusZoom logo" width="120" align="right">
13
+ # pyLocusZoom
14
14
 
15
- Regional association plots for GWAS results with LD coloring, gene tracks, and recombination rate overlays.
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
- - **LD coloring**: SNPs colored by linkage disequilibrium (R²) with lead variant
22
- - **Gene track**: Annotated gene/exon positions below the association plot
23
- - **Recombination rate**: Overlay showing recombination rate across region (*Canis lupus familiaris* only)
24
- - **SNP labels**: Automatic labeling of top SNPs with RS ID or nearest gene
25
- - **Species support**: Built-in *Canis lupus familiaris* (CanFam3.1/CanFam4), *Felis catus* (FelCat9), or custom species
26
- - **CanFam4 support**: Automatic coordinate liftover for recombination maps
27
- - **Multiple backends**: matplotlib (static), plotly (interactive), bokeh (dashboards)
28
- - **Stacked plots**: Compare multiple GWAS/phenotypes vertically
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
  ![Example regional association plot](examples/regional_plot.png)
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 track, recombination overlay, and LD legend. SNP labels (auto-positioned with adjustText) are matplotlib-only.
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
+ ![Example stacked plot](examples/stacked_plot.png)
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
+ ![Example eQTL overlay plot](examples/eqtl_overlay.png)
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
+ ![Example fine-mapping plot](examples/finemapping_plot.png)
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
- ```python
133
- class PlotBackend(Protocol):
134
- def create_figure(self, n_panels, height_ratios, figsize) -> Any
135
- def create_scatter(self, ax, x, y, colors, sizes, hover_data, **kwargs) -> None
136
- def create_line(self, ax, x, y, **kwargs) -> None
137
- def add_annotation(self, ax, text, xy, **kwargs) -> None
138
- def finalize(self, fig, **kwargs) -> Any
139
- def save(self, fig, path, **kwargs) -> None
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