pylocuszoom 0.5.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 (68) hide show
  1. pylocuszoom-0.6.0/.github/workflows/publish.yml +108 -0
  2. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/CHANGELOG.md +25 -2
  3. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/PKG-INFO +53 -5
  4. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/README.md +47 -3
  5. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/bioconda/meta.yaml +2 -2
  6. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/docs/USER_GUIDE.md +159 -1
  7. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/eqtl_bokeh.html +5 -5
  8. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/eqtl_plotly.html +1 -1
  9. pylocuszoom-0.6.0/examples/finemapping_bokeh.html +61 -0
  10. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/finemapping_plotly.html +1 -1
  11. pylocuszoom-0.6.0/examples/forest_plot.png +0 -0
  12. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/generate_readme_plots.py +50 -0
  13. pylocuszoom-0.6.0/examples/phewas_plot.png +0 -0
  14. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/pyproject.toml +6 -2
  15. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/__init__.py +23 -2
  16. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/base.py +86 -0
  17. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/bokeh_backend.py +116 -20
  18. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/matplotlib_backend.py +69 -0
  19. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/plotly_backend.py +115 -23
  20. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/colors.py +41 -0
  21. pylocuszoom-0.6.0/src/pylocuszoom/forest.py +37 -0
  22. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/loaders.py +35 -17
  23. pylocuszoom-0.6.0/src/pylocuszoom/phewas.py +35 -0
  24. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/plotter.py +258 -4
  25. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/recombination.py +45 -31
  26. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/schemas.py +37 -26
  27. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_colors.py +25 -0
  28. pylocuszoom-0.6.0/tests/test_forest.py +54 -0
  29. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_loaders.py +134 -0
  30. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_notebook_backends.py +133 -0
  31. pylocuszoom-0.6.0/tests/test_phewas.py +51 -0
  32. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_plotter.py +156 -0
  33. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/uv.lock +187 -2
  34. pylocuszoom-0.5.0/.github/workflows/publish.yml +0 -23
  35. pylocuszoom-0.5.0/examples/finemapping_bokeh.html +0 -61
  36. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/.gitattributes +0 -0
  37. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  38. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  39. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  40. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/.github/workflows/ci.yml +0 -0
  41. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/.gitignore +0 -0
  42. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/.pre-commit-config.yaml +0 -0
  43. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/CONTRIBUTING.md +0 -0
  44. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/LICENSE.md +0 -0
  45. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/docs/ARCHITECTURE.md +0 -0
  46. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/eqtl_overlay.png +0 -0
  47. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/finemapping_plot.png +0 -0
  48. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/getting_started.ipynb +0 -0
  49. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/regional_plot.png +0 -0
  50. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/examples/stacked_plot.png +0 -0
  51. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/logo.svg +0 -0
  52. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/backends/__init__.py +0 -0
  53. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/eqtl.py +0 -0
  54. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/finemapping.py +0 -0
  55. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/gene_track.py +0 -0
  56. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/labels.py +0 -0
  57. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/ld.py +0 -0
  58. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/logging.py +0 -0
  59. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/py.typed +0 -0
  60. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/reference_data/__init__.py +0 -0
  61. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/src/pylocuszoom/utils.py +0 -0
  62. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/conftest.py +0 -0
  63. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_finemapping.py +0 -0
  64. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_gene_track.py +0 -0
  65. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_labels.py +0 -0
  66. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_ld.py +0 -0
  67. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_logging.py +0 -0
  68. {pylocuszoom-0.5.0 → pylocuszoom-0.6.0}/tests/test_recombination.py +0 -0
@@ -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
@@ -5,7 +5,29 @@ 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]
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
9
31
 
10
32
  ## [0.5.0] - 2026-01-27
11
33
 
@@ -118,7 +140,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
118
140
  - bokeh >= 3.8.2
119
141
  - kaleido >= 0.2.0
120
142
 
121
- [Unreleased]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.5.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
122
145
  [0.5.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.4.0...v0.5.0
123
146
  [0.4.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.3.0...v0.4.0
124
147
  [0.3.0]: https://github.com/michael-denyer/pyLocusZoom/compare/v0.2.0...v0.3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pylocuszoom
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
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
@@ -26,13 +26,17 @@ 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
30
  Requires-Dist: pydantic>=2.0.0
31
31
  Requires-Dist: pyliftover>=0.4
32
+ Requires-Dist: requests>=2.25.0
33
+ Requires-Dist: tqdm>=4.60.0
32
34
  Provides-Extra: all
33
35
  Requires-Dist: pyspark>=3.0.0; extra == 'all'
34
36
  Provides-Extra: dev
35
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'
36
40
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
37
41
  Requires-Dist: ruff>=0.1.0; extra == 'dev'
38
42
  Provides-Extra: spark
@@ -73,9 +77,11 @@ Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.c
73
77
  2. **Stacked plots**: Compare multiple GWAS/phenotypes vertically
74
78
  3. **eQTL plot**: Expression QTL data aligned with association plots and gene tracks
75
79
  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
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
79
85
 
80
86
  ## Installation
81
87
 
@@ -261,6 +267,48 @@ fig = plotter.plot_stacked(
261
267
 
262
268
  ![Example fine-mapping plot](examples/finemapping_plot.png)
263
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
+
264
312
  ## PySpark Support
265
313
 
266
314
  For large-scale genomics data, pass PySpark DataFrames directly:
@@ -32,9 +32,11 @@ Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.c
32
32
  2. **Stacked plots**: Compare multiple GWAS/phenotypes vertically
33
33
  3. **eQTL plot**: Expression QTL data aligned with association plots and gene tracks
34
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
35
+ 5. **PheWAS plots**: Phenome-wide association study visualization across multiple phenotypes
36
+ 6. **Forest plots**: Meta-analysis effect size visualization with confidence intervals
37
+ 7. **Multiple charting libraries**: matplotlib (static), plotly (interactive), bokeh (dashboards)
38
+ 8. **Pandas and PySpark support**: Works with both Pandas and PySpark DataFrames for large-scale genomics data
39
+ 9. **Convenience data file loaders**: Load and validate common GWAS, eQTL and fine-mapping file formats
38
40
 
39
41
  ## Installation
40
42
 
@@ -220,6 +222,48 @@ fig = plotter.plot_stacked(
220
222
 
221
223
  ![Example fine-mapping plot](examples/finemapping_plot.png)
222
224
 
225
+ ## PheWAS Plots
226
+
227
+ Visualize associations of a single variant across multiple phenotypes:
228
+
229
+ ```python
230
+ phewas_df = pd.DataFrame({
231
+ "phenotype": ["Height", "BMI", "T2D", "CAD", "HDL"],
232
+ "p_value": [1e-15, 0.05, 1e-8, 1e-3, 1e-10],
233
+ "category": ["Anthropometric", "Anthropometric", "Metabolic", "Cardiovascular", "Lipids"],
234
+ })
235
+
236
+ fig = plotter.plot_phewas(
237
+ phewas_df,
238
+ variant_id="rs12345",
239
+ category_col="category",
240
+ )
241
+ ```
242
+
243
+ ![Example PheWAS plot](examples/phewas_plot.png)
244
+
245
+ ## Forest Plots
246
+
247
+ Create forest plots for meta-analysis visualization:
248
+
249
+ ```python
250
+ forest_df = pd.DataFrame({
251
+ "study": ["Study A", "Study B", "Study C", "Meta-analysis"],
252
+ "effect": [0.45, 0.52, 0.38, 0.46],
253
+ "ci_lower": [0.30, 0.35, 0.20, 0.40],
254
+ "ci_upper": [0.60, 0.69, 0.56, 0.52],
255
+ "weight": [25, 35, 20, 100],
256
+ })
257
+
258
+ fig = plotter.plot_forest(
259
+ forest_df,
260
+ variant_id="rs12345",
261
+ weight_col="weight",
262
+ )
263
+ ```
264
+
265
+ ![Example forest plot](examples/forest_plot.png)
266
+
223
267
  ## PySpark Support
224
268
 
225
269
  For large-scale genomics data, pass PySpark DataFrames directly:
@@ -1,5 +1,5 @@
1
1
  {% set name = "pylocuszoom" %}
2
- {% set version = "0.3.0" %}
2
+ {% set version = "0.5.0" %}
3
3
 
4
4
  package:
5
5
  name: {{ name|lower }}
@@ -7,7 +7,7 @@ package:
7
7
 
8
8
  source:
9
9
  url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz
10
- sha256: REPLACE_WITH_SHA256_AFTER_PYPI_PUBLISH
10
+ sha256: 5aa46c51631e2c736867b85144f390da94f813146eeef5f943038a772820022e
11
11
 
12
12
  build:
13
13
  noarch: python
@@ -11,6 +11,8 @@ Comprehensive documentation for pyLocusZoom - regional association plots for GWA
11
11
  - [Stacked Plots](#stacked-plots)
12
12
  - [eQTL Overlay](#eqtl-overlay)
13
13
  - [Fine-mapping Visualization](#fine-mapping-visualization)
14
+ - [PheWAS Plots](#phewas-plots)
15
+ - [Forest Plots](#forest-plots)
14
16
  - [Backends](#backends)
15
17
  - [Matplotlib (Static)](#matplotlib-static)
16
18
  - [Plotly (Interactive)](#plotly-interactive)
@@ -19,6 +21,8 @@ Comprehensive documentation for pyLocusZoom - regional association plots for GWA
19
21
  - [LocusZoomPlotter](#locuszoomplotter)
20
22
  - [plot() Method](#plot-method)
21
23
  - [plot_stacked() Method](#plot_stacked-method)
24
+ - [plot_phewas() Method](#plot_phewas-method)
25
+ - [plot_forest() Method](#plot_forest-method)
22
26
  - [File Loaders](#file-loaders)
23
27
  - [GWAS Loaders](#gwas-loaders)
24
28
  - [eQTL Loaders](#eqtl-loaders)
@@ -209,6 +213,63 @@ fig = plotter.plot_stacked(
209
213
  - Credible sets colored distinctly (CS1 = red, CS2 = blue, etc.)
210
214
  - Variants not in credible sets shown in gray
211
215
 
216
+ ### PheWAS Plots
217
+
218
+ Visualize associations of a single variant across multiple phenotypes in a phenome-wide association study.
219
+
220
+ ![PheWAS plot](../examples/phewas_plot.png)
221
+
222
+ ```python
223
+ phewas_df = pd.DataFrame({
224
+ "phenotype": ["Height", "BMI", "T2D", "CAD", "HDL"],
225
+ "p_value": [1e-15, 0.05, 1e-8, 1e-3, 1e-10],
226
+ "category": ["Anthropometric", "Anthropometric", "Metabolic", "Cardiovascular", "Lipids"],
227
+ })
228
+
229
+ fig = plotter.plot_phewas(
230
+ phewas_df,
231
+ variant_id="rs12345",
232
+ category_col="category",
233
+ significance_threshold=5e-8,
234
+ )
235
+ ```
236
+
237
+ **Features:**
238
+ - Phenotypes grouped and colored by category
239
+ - Genome-wide significance line (red dashed)
240
+ - Optional effect direction markers (triangles for +/-)
241
+ - 12-color palette for distinct categories
242
+
243
+ ### Forest Plots
244
+
245
+ Create forest plots for meta-analysis visualization showing effect sizes with confidence intervals.
246
+
247
+ ![Forest plot](../examples/forest_plot.png)
248
+
249
+ ```python
250
+ forest_df = pd.DataFrame({
251
+ "study": ["Study A", "Study B", "Study C", "Meta-analysis"],
252
+ "effect": [0.45, 0.52, 0.38, 0.46],
253
+ "ci_lower": [0.30, 0.35, 0.20, 0.40],
254
+ "ci_upper": [0.60, 0.69, 0.56, 0.52],
255
+ "weight": [25, 35, 20, 100], # Optional: affects marker size
256
+ })
257
+
258
+ fig = plotter.plot_forest(
259
+ forest_df,
260
+ variant_id="rs12345",
261
+ weight_col="weight",
262
+ null_value=0.0, # Reference line (0 for beta, 1 for OR)
263
+ effect_label="Effect Size",
264
+ )
265
+ ```
266
+
267
+ **Features:**
268
+ - Effect sizes as squares with confidence interval whiskers
269
+ - Marker size scaled by study weight (optional)
270
+ - Null effect reference line
271
+ - Study names as y-axis labels
272
+
212
273
  ---
213
274
 
214
275
  ## Backends
@@ -391,6 +452,66 @@ fig = plotter.plot_stacked(
391
452
  | `finemapping_df` | DataFrame | None | Fine-mapping results with `pos` and `pip`. |
392
453
  | `finemapping_cs_col` | str | `"cs"` | Column for credible set assignment. |
393
454
 
455
+ ### plot_phewas() Method
456
+
457
+ Create a PheWAS (Phenome-Wide Association Study) plot.
458
+
459
+ ```python
460
+ fig = plotter.plot_phewas(
461
+ phewas_df, # Required: PheWAS results
462
+ variant_id="rs12345", # Required: variant ID for title
463
+ phenotype_col="phenotype", # Phenotype name column
464
+ p_col="p_value", # P-value column
465
+ category_col="category", # Category for grouping/coloring
466
+ effect_col=None, # Effect column for direction markers
467
+ significance_threshold=5e-8, # Significance line threshold
468
+ figsize=(10, 8), # Figure dimensions
469
+ )
470
+ ```
471
+
472
+ | Parameter | Type | Default | Description |
473
+ |-----------|------|---------|-------------|
474
+ | `phewas_df` | DataFrame | Required | PheWAS results with phenotype and p-value. |
475
+ | `variant_id` | str | Required | Variant ID for plot title. |
476
+ | `phenotype_col` | str | `"phenotype"` | Column with phenotype names. |
477
+ | `p_col` | str | `"p_value"` | Column with p-values. |
478
+ | `category_col` | str | `"category"` | Column for phenotype categories. |
479
+ | `effect_col` | str | None | Column with effect sizes for direction markers. |
480
+ | `significance_threshold` | float | `5e-8` | P-value for significance line. |
481
+ | `figsize` | tuple | `(10, 8)` | Figure dimensions (width, height). |
482
+
483
+ ### plot_forest() Method
484
+
485
+ Create a forest plot for meta-analysis visualization.
486
+
487
+ ```python
488
+ fig = plotter.plot_forest(
489
+ forest_df, # Required: meta-analysis data
490
+ variant_id="rs12345", # Required: variant ID for title
491
+ study_col="study", # Study/phenotype name column
492
+ effect_col="effect", # Effect size column
493
+ ci_lower_col="ci_lower", # Lower CI column
494
+ ci_upper_col="ci_upper", # Upper CI column
495
+ weight_col=None, # Optional weight column for marker sizes
496
+ null_value=0.0, # Null effect value (0 for beta, 1 for OR)
497
+ effect_label="Effect Size", # X-axis label
498
+ figsize=(8, 6), # Figure dimensions
499
+ )
500
+ ```
501
+
502
+ | Parameter | Type | Default | Description |
503
+ |-----------|------|---------|-------------|
504
+ | `forest_df` | DataFrame | Required | Forest plot data with effects and CIs. |
505
+ | `variant_id` | str | Required | Variant ID for plot title. |
506
+ | `study_col` | str | `"study"` | Column with study/phenotype names. |
507
+ | `effect_col` | str | `"effect"` | Column with effect sizes. |
508
+ | `ci_lower_col` | str | `"ci_lower"` | Column with lower confidence interval. |
509
+ | `ci_upper_col` | str | `"ci_upper"` | Column with upper confidence interval. |
510
+ | `weight_col` | str | None | Column with study weights (affects marker size). |
511
+ | `null_value` | float | `0.0` | Reference value for null effect line. |
512
+ | `effect_label` | str | `"Effect Size"` | X-axis label. |
513
+ | `figsize` | tuple | `(8, 6)` | Figure dimensions (width, height). |
514
+
394
515
  ---
395
516
 
396
517
  ## File Loaders
@@ -568,7 +689,44 @@ gwas_df = pd.DataFrame({
568
689
  | `pos` | int | Yes | Variant position. |
569
690
  | `p_value` | float | Yes | Association p-value. |
570
691
  | `gene` | str | Yes | Target gene symbol. |
571
- | `effect` | float | No | Effect size for color coding. |
692
+ | `effect_size` | float | No | Effect size for color coding. |
693
+
694
+ ### PheWAS DataFrame
695
+
696
+ | Column | Type | Required | Description |
697
+ |--------|------|----------|-------------|
698
+ | `phenotype` | str | Yes | Phenotype name. |
699
+ | `p_value` | float | Yes | Association p-value. |
700
+ | `category` | str | No | Phenotype category for grouping/coloring. |
701
+ | `effect_size` | float | No | Effect size for direction markers. |
702
+
703
+ ```python
704
+ phewas_df = pd.DataFrame({
705
+ "phenotype": ["Height", "BMI", "T2D"],
706
+ "p_value": [1e-15, 0.05, 1e-8],
707
+ "category": ["Anthropometric", "Anthropometric", "Metabolic"],
708
+ })
709
+ ```
710
+
711
+ ### Forest Plot DataFrame
712
+
713
+ | Column | Type | Required | Description |
714
+ |--------|------|----------|-------------|
715
+ | `study` | str | Yes | Study or phenotype name. |
716
+ | `effect` | float | Yes | Effect size (beta, OR, HR). |
717
+ | `ci_lower` | float | Yes | Lower confidence interval bound. |
718
+ | `ci_upper` | float | Yes | Upper confidence interval bound. |
719
+ | `weight` | float | No | Study weight (affects marker size). |
720
+
721
+ ```python
722
+ forest_df = pd.DataFrame({
723
+ "study": ["Study A", "Study B", "Meta-analysis"],
724
+ "effect": [0.45, 0.52, 0.46],
725
+ "ci_lower": [0.30, 0.35, 0.40],
726
+ "ci_upper": [0.60, 0.69, 0.52],
727
+ "weight": [25, 35, 100],
728
+ })
729
+ ```
572
730
 
573
731
  ---
574
732