pylocuszoom 0.3.0__tar.gz → 0.6.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 (72) hide show
  1. pylocuszoom-0.6.0/.gitattributes +3 -0
  2. pylocuszoom-0.6.0/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  3. pylocuszoom-0.6.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. pylocuszoom-0.6.0/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  5. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/.github/workflows/ci.yml +1 -1
  6. pylocuszoom-0.6.0/.github/workflows/publish.yml +108 -0
  7. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/.gitignore +3 -0
  8. pylocuszoom-0.6.0/.pre-commit-config.yaml +7 -0
  9. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/CHANGELOG.md +64 -1
  10. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/PKG-INFO +153 -25
  11. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/README.md +145 -22
  12. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/bioconda/meta.yaml +2 -2
  13. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/docs/ARCHITECTURE.md +30 -8
  14. pylocuszoom-0.6.0/docs/USER_GUIDE.md +898 -0
  15. pylocuszoom-0.6.0/examples/eqtl_bokeh.html +61 -0
  16. pylocuszoom-0.6.0/examples/eqtl_overlay.png +0 -0
  17. pylocuszoom-0.6.0/examples/eqtl_plotly.html +3888 -0
  18. pylocuszoom-0.6.0/examples/finemapping_bokeh.html +61 -0
  19. pylocuszoom-0.6.0/examples/finemapping_plot.png +0 -0
  20. pylocuszoom-0.6.0/examples/finemapping_plotly.html +3888 -0
  21. pylocuszoom-0.6.0/examples/forest_plot.png +0 -0
  22. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/examples/generate_readme_plots.py +128 -2
  23. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/examples/getting_started.ipynb +3 -79
  24. pylocuszoom-0.6.0/examples/phewas_plot.png +0 -0
  25. pylocuszoom-0.6.0/examples/regional_plot.png +0 -0
  26. pylocuszoom-0.6.0/examples/stacked_plot.png +0 -0
  27. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/pyproject.toml +8 -3
  28. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/__init__.py +74 -2
  29. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/base.py +131 -0
  30. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/bokeh_backend.py +254 -68
  31. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/matplotlib_backend.py +173 -0
  32. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/plotly_backend.py +327 -87
  33. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/colors.py +44 -1
  34. pylocuszoom-0.6.0/src/pylocuszoom/forest.py +37 -0
  35. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/gene_track.py +1 -0
  36. pylocuszoom-0.6.0/src/pylocuszoom/loaders.py +880 -0
  37. pylocuszoom-0.6.0/src/pylocuszoom/phewas.py +35 -0
  38. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/plotter.py +342 -117
  39. pylocuszoom-0.6.0/src/pylocuszoom/py.typed +0 -0
  40. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/recombination.py +49 -35
  41. pylocuszoom-0.6.0/src/pylocuszoom/schemas.py +406 -0
  42. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/test_colors.py +25 -0
  43. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/test_finemapping.py +6 -2
  44. pylocuszoom-0.6.0/tests/test_forest.py +54 -0
  45. pylocuszoom-0.6.0/tests/test_loaders.py +600 -0
  46. pylocuszoom-0.6.0/tests/test_notebook_backends.py +931 -0
  47. pylocuszoom-0.6.0/tests/test_phewas.py +51 -0
  48. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/test_plotter.py +392 -0
  49. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/uv.lock +343 -2
  50. pylocuszoom-0.3.0/.github/workflows/publish.yml +0 -23
  51. pylocuszoom-0.3.0/examples/eqtl_overlay.png +0 -0
  52. pylocuszoom-0.3.0/examples/finemapping_plot.png +0 -0
  53. pylocuszoom-0.3.0/examples/regional_plot.png +0 -0
  54. pylocuszoom-0.3.0/examples/stacked_plot.png +0 -0
  55. pylocuszoom-0.3.0/tests/test_notebook_backends.py +0 -457
  56. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/CONTRIBUTING.md +0 -0
  57. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/LICENSE.md +0 -0
  58. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/logo.svg +0 -0
  59. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/__init__.py +0 -0
  60. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/eqtl.py +0 -0
  61. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/finemapping.py +0 -0
  62. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/labels.py +0 -0
  63. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/ld.py +0 -0
  64. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/logging.py +0 -0
  65. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/reference_data/__init__.py +0 -0
  66. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/src/pylocuszoom/utils.py +0 -0
  67. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/conftest.py +0 -0
  68. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/test_gene_track.py +0 -0
  69. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/test_labels.py +0 -0
  70. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/test_ld.py +0 -0
  71. {pylocuszoom-0.3.0 → pylocuszoom-0.6.0}/tests/test_logging.py +0 -0
  72. {pylocuszoom-0.3.0 → pylocuszoom-0.6.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
@@ -0,0 +1,108 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+ outputs:
14
+ version: ${{ steps.version.outputs.version }}
15
+ sha256: ${{ steps.sha256.outputs.sha256 }}
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v5
21
+
22
+ - name: Get version
23
+ id: version
24
+ run: |
25
+ VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
26
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
27
+
28
+ - name: Build package
29
+ run: uv build
30
+
31
+ - name: Publish to PyPI
32
+ uses: pypa/gh-action-pypi-publish@release/v1
33
+
34
+ - name: Wait for PyPI availability
35
+ env:
36
+ PKG_VERSION: ${{ steps.version.outputs.version }}
37
+ run: |
38
+ echo "Waiting for pylocuszoom $PKG_VERSION to be available on PyPI..."
39
+ for i in {1..30}; do
40
+ if curl -s "https://pypi.org/pypi/pylocuszoom/$PKG_VERSION/json" | grep -q '"version"'; then
41
+ echo "Package available on PyPI"
42
+ break
43
+ fi
44
+ echo "Attempt $i: Package not yet available, waiting 10s..."
45
+ sleep 10
46
+ done
47
+
48
+ - name: Get SHA256
49
+ id: sha256
50
+ env:
51
+ PKG_VERSION: ${{ steps.version.outputs.version }}
52
+ run: |
53
+ URL="https://pypi.io/packages/source/p/pylocuszoom/pylocuszoom-$PKG_VERSION.tar.gz"
54
+ SHA256=$(curl -sL "$URL" | sha256sum | cut -d' ' -f1)
55
+ echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
56
+ echo "SHA256: $SHA256"
57
+
58
+ update-bioconda:
59
+ needs: publish
60
+ runs-on: ubuntu-latest
61
+ steps:
62
+ - uses: actions/checkout@v4
63
+
64
+ - name: Update bioconda/meta.yaml
65
+ env:
66
+ PKG_VERSION: ${{ needs.publish.outputs.version }}
67
+ PKG_SHA256: ${{ needs.publish.outputs.sha256 }}
68
+ run: |
69
+ # Update version
70
+ sed -i "s/{% set version = \".*\" %}/{% set version = \"$PKG_VERSION\" %}/" bioconda/meta.yaml
71
+
72
+ # Update sha256
73
+ sed -i "s/sha256: .*/sha256: $PKG_SHA256/" bioconda/meta.yaml
74
+
75
+ # Update plotly version requirement
76
+ sed -i "s/plotly >=5.0.0/plotly >=5.15.0/" bioconda/meta.yaml
77
+
78
+ # Add new dependencies if missing
79
+ if ! grep -q "pydantic" bioconda/meta.yaml; then
80
+ sed -i '/adjusttext/a\ - pydantic >=2.0.0' bioconda/meta.yaml
81
+ fi
82
+ if ! grep -q "requests" bioconda/meta.yaml; then
83
+ sed -i '/pydantic/a\ - requests >=2.25.0' bioconda/meta.yaml
84
+ fi
85
+ if ! grep -q "tqdm" bioconda/meta.yaml; then
86
+ sed -i '/requests/a\ - tqdm >=4.60.0' bioconda/meta.yaml
87
+ fi
88
+
89
+ cat bioconda/meta.yaml
90
+
91
+ - name: Create Pull Request
92
+ uses: peter-evans/create-pull-request@v6
93
+ with:
94
+ token: ${{ secrets.GITHUB_TOKEN }}
95
+ commit-message: "chore: update bioconda recipe for new release"
96
+ branch: bioconda-update
97
+ title: "Update bioconda recipe"
98
+ body: |
99
+ Automated update of bioconda/meta.yaml after PyPI release.
100
+
101
+ **Next steps:**
102
+ 1. Review this PR
103
+ 2. Merge to main
104
+ 3. Copy `bioconda/meta.yaml` to your fork of bioconda-recipes
105
+ 4. Submit PR to bioconda-recipes
106
+ labels: |
107
+ bioconda
108
+ automated
@@ -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,66 @@ 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
+ ## [0.6.0] - 2026-01-27
9
+
10
+ ### Added
11
+ - `plot_phewas()` method for phenome-wide association study plots
12
+ - `plot_forest()` method for forest plots (meta-analysis visualization)
13
+ - PheWAS category color palette with 12 distinct colors
14
+ - Forest plot and PheWAS validation utilities
15
+ - Backend methods: `axvline()`, `hbar()`, `errorbar_h()` for new plot types
16
+ - Example plots for PheWAS and forest plots
17
+ - Progress bars (tqdm) for recombination map and liftover chain downloads
18
+ - `requests` and `tqdm` as core dependencies for reliable downloads with progress
19
+ - `pytest-randomly` and `pytest-xdist` as dev dependencies for test randomization and parallel execution
20
+
21
+ ### Changed
22
+ - Bumped minimum Plotly version to 5.15.0 (required for multiple legends feature)
23
+ - eQTL loaders now output `effect_size` column instead of `effect` for plotter compatibility
24
+ - Download functions now use `requests` with streaming and progress bars instead of `urllib`
25
+
26
+ ### Fixed
27
+ - SAIGE loader now prefers SPA-adjusted p-values (`p.value.NA`) over raw p-values when both present
28
+ - BED loader now handles BED12 format and files with more than 6 columns
29
+ - eQTL panel in `plot_stacked()` now filters by chromosome in addition to position
30
+ - Validation errors for non-numeric p-values or positions now show clear "must be numeric" message instead of runtime errors
31
+
32
+ ## [0.5.0] - 2026-01-27
33
+
34
+ ### Added
35
+ - Hover tooltips for fine-mapping scatter plots (Plotly/Bokeh backends)
36
+ - Hover tooltips for eQTL scatter plots (Plotly/Bokeh backends)
37
+ - Interactive HTML example plots for eQTL and fine-mapping (Plotly/Bokeh)
38
+ - Comprehensive marker and hover data tests for interactive backends
39
+
40
+ ### Changed
41
+ - Plotly/Bokeh backends now hide grid lines for cleaner LocusZoom appearance
42
+ - Plotly/Bokeh backends now show black axis lines (matching matplotlib style)
43
+ - Plotly/Bokeh gene track panels now hide y-axis (ticks, labels, line, grid)
44
+ - Plotly/Bokeh backends now hide minor ticks and zero lines
45
+
46
+ ## [0.4.0] - 2026-01-26
47
+
48
+ ### Added
49
+ - **File format loaders** for common GWAS, eQTL, and fine-mapping formats:
50
+ - GWAS: `load_gwas`, `load_plink_assoc`, `load_regenie`, `load_bolt_lmm`, `load_gemma`, `load_saige`, `load_gwas_catalog`
51
+ - eQTL: `load_gtex_eqtl`, `load_eqtl_catalogue`, `load_matrixeqtl`
52
+ - Fine-mapping: `load_susie`, `load_finemap`, `load_caviar`, `load_polyfun`
53
+ - Gene annotations: `load_gtf`, `load_bed`, `load_ensembl_genes`
54
+ - Pydantic validation for file loaders with detailed error messages
55
+ - `py.typed` marker for PEP 561 type checking support
56
+ - Pre-commit configuration for automated linting
57
+ - GitHub issue templates for bug reports and feature requests
58
+ - Codecov badge in README
59
+
60
+ ### Changed
61
+ - eQTL and fine-mapping legends now route through backend protocol (works with all backends)
62
+ - Simplified backend code with reduced duplication
63
+ - Backend protocol class diagram added to ARCHITECTURE.md
64
+
65
+ ### Fixed
66
+ - Additional robustness improvements for edge cases
67
+
8
68
  ## [0.3.0] - 2026-01-26
9
69
 
10
70
  ### Added
@@ -80,7 +140,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
80
140
  - bokeh >= 3.8.2
81
141
  - kaleido >= 0.2.0
82
142
 
83
- [Unreleased]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.3.0...HEAD
143
+ [Unreleased]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.6.0...HEAD
144
+ [0.6.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.5.0...v0.6.0
145
+ [0.5.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.4.0...v0.5.0
146
+ [0.4.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.3.0...v0.4.0
84
147
  [0.3.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.2.0...v0.3.0
85
148
  [0.2.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.1.0...v0.2.0
86
149
  [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.6.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
@@ -26,51 +26,63 @@ Requires-Dist: loguru>=0.7.0
26
26
  Requires-Dist: matplotlib>=3.5.0
27
27
  Requires-Dist: numpy>=1.21.0
28
28
  Requires-Dist: pandas>=1.4.0
29
- Requires-Dist: plotly>=5.0.0
29
+ Requires-Dist: plotly>=5.15.0
30
+ Requires-Dist: pydantic>=2.0.0
30
31
  Requires-Dist: pyliftover>=0.4
32
+ Requires-Dist: requests>=2.25.0
33
+ Requires-Dist: tqdm>=4.60.0
31
34
  Provides-Extra: all
32
35
  Requires-Dist: pyspark>=3.0.0; extra == 'all'
33
36
  Provides-Extra: dev
34
37
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
38
+ Requires-Dist: pytest-randomly>=3.0.0; extra == 'dev'
39
+ Requires-Dist: pytest-xdist>=3.0.0; extra == 'dev'
35
40
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
36
41
  Requires-Dist: ruff>=0.1.0; extra == 'dev'
37
42
  Provides-Extra: spark
38
43
  Requires-Dist: pyspark>=3.0.0; extra == 'spark'
39
44
  Description-Content-Type: text/markdown
40
45
 
41
- # pyLocusZoom
42
-
43
46
  [![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)
47
+ [![codecov](https://codecov.io/gh/michael-denyer/pyLocusZoom/graph/badge.svg)](https://codecov.io/gh/michael-denyer/pyLocusZoom)
48
+ [![PyPI](https://img.shields.io/pypi/v/pylocuszoom)](https://pypi.org/project/pylocuszoom/)
49
+ [![Bioconda](https://img.shields.io/conda/vn/bioconda/pylocuszoom)](https://anaconda.org/bioconda/pylocuszoom)
50
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg)](https://www.gnu.org/licenses/gpl-3.0)
45
51
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
46
52
  [![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
53
  [![Matplotlib](https://img.shields.io/badge/Matplotlib-3.5+-11557c.svg)](https://matplotlib.org/)
49
54
  [![Plotly](https://img.shields.io/badge/Plotly-5.0+-3F4F75.svg)](https://plotly.com/python/)
50
55
  [![Bokeh](https://img.shields.io/badge/Bokeh-3.8+-E6526F.svg)](https://bokeh.org/)
51
56
  [![Pandas](https://img.shields.io/badge/Pandas-1.4+-150458.svg)](https://pandas.pydata.org/)
52
-
53
57
  <img src="logo.svg" alt="pyLocusZoom logo" width="120" align="right">
58
+ # pyLocusZoom
54
59
 
55
- Regional association plots for GWAS results with LD coloring, gene tracks, and recombination rate overlays.
60
+ Publication-ready regional association plots with LD coloring, gene tracks, and recombination overlays.
56
61
 
57
62
  Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.com/myles-lewis/locuszoomr).
58
63
 
59
64
  ## Features
60
65
 
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
66
+ 1. **Regional association plot**:
67
+
68
+ - **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
69
+ - **LD coloring**: SNPs colored by linkage disequilibrium (R²) with lead variant
70
+ - **Gene tracks**: Annotated gene/exon positions below the association plot
71
+ - **Recombination rate**: Overlay showing recombination rate across region (*Canis lupus familiaris* only)
72
+ - **SNP labels (matplotlib)**: Automatic labeling of lead SNPs with RS ID
73
+ - **Tooltips (Bokeh and Plotly)**: Mouseover for detailed SNP data
71
74
 
72
75
  ![Example regional association plot](examples/regional_plot.png)
73
76
 
77
+ 2. **Stacked plots**: Compare multiple GWAS/phenotypes vertically
78
+ 3. **eQTL plot**: Expression QTL data aligned with association plots and gene tracks
79
+ 4. **Fine-mapping plots**: Visualize SuSiE credible sets with posterior inclusion probabilities
80
+ 5. **PheWAS plots**: Phenome-wide association study visualization across multiple phenotypes
81
+ 6. **Forest plots**: Meta-analysis effect size visualization with confidence intervals
82
+ 7. **Multiple charting libraries**: matplotlib (static), plotly (interactive), bokeh (dashboards)
83
+ 8. **Pandas and PySpark support**: Works with both Pandas and PySpark DataFrames for large-scale genomics data
84
+ 9. **Convenience data file loaders**: Load and validate common GWAS, eQTL and fine-mapping file formats
85
+
74
86
  ## Installation
75
87
 
76
88
  ```bash
@@ -186,13 +198,13 @@ fig.write_html("plot.html")
186
198
  fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="bokeh")
187
199
  ```
188
200
 
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 |
201
+ | Backend | Output | Best For | Features |
202
+ |---------|--------|----------|----------|
203
+ | `matplotlib` | Static PNG/PDF/SVG | Publications, presentations | Full feature set with SNP labels |
204
+ | `plotly` | Interactive HTML | Web reports, data exploration | Hover tooltips, pan/zoom |
205
+ | `bokeh` | Interactive HTML | Dashboards, web apps | Hover tooltips, pan/zoom |
194
206
 
195
- > **Note:** All backends support gene track, recombination overlay, and LD legend. SNP labels (auto-positioned with adjustText) are matplotlib-only.
207
+ > **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
208
 
197
209
  ## Stacked Plots
198
210
 
@@ -209,6 +221,8 @@ fig = plotter.plot_stacked(
209
221
  )
210
222
  ```
211
223
 
224
+ ![Example stacked plot](examples/stacked_plot.png)
225
+
212
226
  ## eQTL Overlay
213
227
 
214
228
  Add expression QTL data as a separate panel:
@@ -229,6 +243,72 @@ fig = plotter.plot_stacked(
229
243
  )
230
244
  ```
231
245
 
246
+ ![Example eQTL overlay plot](examples/eqtl_overlay.png)
247
+
248
+ ## Fine-mapping Visualization
249
+
250
+ Visualize SuSiE or other fine-mapping results with credible set coloring:
251
+
252
+ ```python
253
+ finemapping_df = pd.DataFrame({
254
+ "pos": [1000500, 1001200, 1002000, 1003500],
255
+ "pip": [0.85, 0.12, 0.02, 0.45], # Posterior inclusion probability
256
+ "cs": [1, 1, 0, 2], # Credible set assignment (0 = not in CS)
257
+ })
258
+
259
+ fig = plotter.plot_stacked(
260
+ [gwas_df],
261
+ chrom=1, start=1000000, end=2000000,
262
+ finemapping_df=finemapping_df,
263
+ finemapping_cs_col="cs",
264
+ genes_df=genes_df,
265
+ )
266
+ ```
267
+
268
+ ![Example fine-mapping plot](examples/finemapping_plot.png)
269
+
270
+ ## PheWAS Plots
271
+
272
+ Visualize associations of a single variant across multiple phenotypes:
273
+
274
+ ```python
275
+ phewas_df = pd.DataFrame({
276
+ "phenotype": ["Height", "BMI", "T2D", "CAD", "HDL"],
277
+ "p_value": [1e-15, 0.05, 1e-8, 1e-3, 1e-10],
278
+ "category": ["Anthropometric", "Anthropometric", "Metabolic", "Cardiovascular", "Lipids"],
279
+ })
280
+
281
+ fig = plotter.plot_phewas(
282
+ phewas_df,
283
+ variant_id="rs12345",
284
+ category_col="category",
285
+ )
286
+ ```
287
+
288
+ ![Example PheWAS plot](examples/phewas_plot.png)
289
+
290
+ ## Forest Plots
291
+
292
+ Create forest plots for meta-analysis visualization:
293
+
294
+ ```python
295
+ forest_df = pd.DataFrame({
296
+ "study": ["Study A", "Study B", "Study C", "Meta-analysis"],
297
+ "effect": [0.45, 0.52, 0.38, 0.46],
298
+ "ci_lower": [0.30, 0.35, 0.20, 0.40],
299
+ "ci_upper": [0.60, 0.69, 0.56, 0.52],
300
+ "weight": [25, 35, 20, 100],
301
+ })
302
+
303
+ fig = plotter.plot_forest(
304
+ forest_df,
305
+ variant_id="rs12345",
306
+ weight_col="weight",
307
+ )
308
+ ```
309
+
310
+ ![Example forest plot](examples/forest_plot.png)
311
+
232
312
  ## PySpark Support
233
313
 
234
314
  For large-scale genomics data, pass PySpark DataFrames directly:
@@ -245,6 +325,47 @@ pandas_df = to_pandas(spark_gwas_df, sample_size=100000)
245
325
 
246
326
  Install PySpark support: `uv add pylocuszoom[spark]`
247
327
 
328
+ ## Loading Data from Files
329
+
330
+ pyLocusZoom includes loaders for common GWAS, eQTL, and fine-mapping file formats:
331
+
332
+ ```python
333
+ from pylocuszoom import (
334
+ # GWAS loaders
335
+ load_gwas, # Auto-detect format
336
+ load_plink_assoc, # PLINK .assoc, .assoc.linear, .qassoc
337
+ load_regenie, # REGENIE .regenie
338
+ load_bolt_lmm, # BOLT-LMM .stats
339
+ load_gemma, # GEMMA .assoc.txt
340
+ load_saige, # SAIGE output
341
+ # eQTL loaders
342
+ load_gtex_eqtl, # GTEx significant pairs
343
+ load_eqtl_catalogue, # eQTL Catalogue format
344
+ # Fine-mapping loaders
345
+ load_susie, # SuSiE output
346
+ load_finemap, # FINEMAP .snp output
347
+ # Gene annotations
348
+ load_gtf, # GTF/GFF3 files
349
+ load_bed, # BED files
350
+ )
351
+
352
+ # Auto-detect GWAS format from filename
353
+ gwas_df = load_gwas("results.assoc.linear")
354
+
355
+ # Or use specific loader
356
+ gwas_df = load_regenie("ukb_results.regenie")
357
+
358
+ # Load gene annotations
359
+ genes_df = load_gtf("genes.gtf", feature_type="gene")
360
+ exons_df = load_gtf("genes.gtf", feature_type="exon")
361
+
362
+ # Load eQTL data
363
+ eqtl_df = load_gtex_eqtl("GTEx.signif_pairs.txt.gz", gene="BRCA1")
364
+
365
+ # Load fine-mapping results
366
+ fm_df = load_susie("susie_output.tsv")
367
+ ```
368
+
248
369
  ## Data Formats
249
370
 
250
371
  ### GWAS Results DataFrame
@@ -371,6 +492,13 @@ plotter = LocusZoomPlotter(log_level="DEBUG")
371
492
  Optional:
372
493
  - pyspark >= 3.0.0 (for PySpark DataFrame support) - `uv add pylocuszoom[spark]`
373
494
 
495
+ ## Documentation
496
+
497
+ - [User Guide](docs/USER_GUIDE.md) - Comprehensive documentation with API reference
498
+ - [Architecture](docs/ARCHITECTURE.md) - Design decisions and component overview
499
+ - [Example Notebook](examples/getting_started.ipynb) - Interactive tutorial
500
+ - [CHANGELOG](CHANGELOG.md) - Version history
501
+
374
502
  ## License
375
503
 
376
504
  GPL-3.0-or-later