pylocuszoom 0.5.0__py3-none-any.whl → 0.8.0__py3-none-any.whl

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.
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pylocuszoom
3
- Version: 0.5.0
3
+ Version: 0.8.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
7
7
  Project-URL: Repository, https://github.com/michael-denyer/pylocuszoom
8
- Author: Michael Denyer
8
+ Author-email: Michael Denyer <code.denyer@gmail.com>
9
9
  License-Expression: GPL-3.0-or-later
10
10
  License-File: LICENSE.md
11
11
  Keywords: genetics,gwas,locus-zoom,locuszoom,regional-plot,visualization
12
- Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Science/Research
14
14
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
15
15
  Classifier: Programming Language :: Python :: 3
@@ -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
@@ -40,20 +44,18 @@ Requires-Dist: pyspark>=3.0.0; extra == 'spark'
40
44
  Description-Content-Type: text/markdown
41
45
 
42
46
  [![CI](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml/badge.svg)](https://github.com/michael-denyer/pyLocusZoom/actions/workflows/ci.yml)
43
- [![codecov](https://codecov.io/gh/michael-denyer/pyLocusZoom/graph/badge.svg)](https://codecov.io/gh/michael-denyer/pyLocusZoom)
44
47
  [![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
48
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg)](https://www.gnu.org/licenses/gpl-3.0)
47
49
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
48
50
  [![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)
49
51
  [![Matplotlib](https://img.shields.io/badge/Matplotlib-3.5+-11557c.svg)](https://matplotlib.org/)
50
- [![Plotly](https://img.shields.io/badge/Plotly-5.0+-3F4F75.svg)](https://plotly.com/python/)
52
+ [![Plotly](https://img.shields.io/badge/Plotly-5.15+-3F4F75.svg)](https://plotly.com/python/)
51
53
  [![Bokeh](https://img.shields.io/badge/Bokeh-3.8+-E6526F.svg)](https://bokeh.org/)
52
54
  [![Pandas](https://img.shields.io/badge/Pandas-1.4+-150458.svg)](https://pandas.pydata.org/)
53
55
  <img src="logo.svg" alt="pyLocusZoom logo" width="120" align="right">
54
56
  # pyLocusZoom
55
57
 
56
- Publication-ready regional association plots with LD coloring, gene tracks, and recombination overlays.
58
+ Designed for publication-ready GWAS visualization with regional association plots, gene tracks, eQTL, PheWAS, fine-mapping, and forest plots.
57
59
 
58
60
  Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.com/myles-lewis/locuszoomr).
59
61
 
@@ -64,18 +66,22 @@ Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.c
64
66
  - **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
67
  - **LD coloring**: SNPs colored by linkage disequilibrium (R²) with lead variant
66
68
  - **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
69
+ - **Recombination rate**: Optional overlay across region (*Canis lupus familiaris* built-in, not shown in example image)
70
+ - **SNP labels (matplotlib)**: Automatic labeling of top SNPs by p-value (RS IDs)
71
+ - **Hover tooltips (Plotly and Bokeh)**: Detailed SNP data on hover
70
72
 
71
- ![Example regional association plot](examples/regional_plot.png)
73
+ ![Example regional association plot with LD coloring and gene track](examples/regional_plot.png)
74
+ *Regional association plot with LD coloring, gene/exon track, and top SNP labels (recombination overlay disabled in example).*
72
75
 
73
76
  2. **Stacked plots**: Compare multiple GWAS/phenotypes vertically
74
77
  3. **eQTL plot**: Expression QTL data aligned with association plots and gene tracks
75
78
  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
+ 5. **PheWAS plots**: Phenome-wide association study visualization across multiple phenotypes
80
+ 6. **Forest plots**: Meta-analysis effect size visualization with confidence intervals
81
+ 7. **Multiple backends**: matplotlib (publication-ready), plotly (interactive), bokeh (dashboard integration)
82
+ 8. **Pandas and PySpark support**: Works with both Pandas and PySpark DataFrames for large-scale genomics data
83
+ 9. **Convenience data file loaders**: Load and validate common GWAS, eQTL and fine-mapping file formats
84
+ 10. **Automatic gene annotations**: Fetch gene/exon data from Ensembl REST API with caching (human, mouse, rat, canine, feline, and any Ensembl species)
79
85
 
80
86
  ## Installation
81
87
 
@@ -175,28 +181,46 @@ fig = plotter.plot(
175
181
  )
176
182
  ```
177
183
 
184
+ ## Automatic Gene Annotations
185
+
186
+ pyLocusZoom can automatically fetch gene annotations from Ensembl for any species:
187
+
188
+ ```python
189
+ # Enable automatic gene fetching
190
+ plotter = LocusZoomPlotter(species="human", auto_genes=True)
191
+
192
+ # No need to provide genes_df - fetched automatically
193
+ fig = plotter.plot(gwas_df, chrom=13, start=32000000, end=33000000)
194
+ ```
195
+
196
+ Supported species aliases: `human`, `mouse`, `rat`, `canine`/`dog`, `feline`/`cat`, or any Ensembl species name.
197
+ Data is cached locally for fast subsequent plots. Maximum region size is 5Mb (Ensembl API limit).
198
+
178
199
  ## Backends
179
200
 
180
- pyLocusZoom supports multiple rendering backends:
201
+ pyLocusZoom supports multiple rendering backends (set at initialization):
181
202
 
182
203
  ```python
183
204
  # Static publication-quality plot (default)
184
- fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="matplotlib")
205
+ plotter = LocusZoomPlotter(species="canine", backend="matplotlib")
206
+ fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
185
207
  fig.savefig("plot.png", dpi=150)
186
208
 
187
209
  # Interactive Plotly (hover tooltips, pan/zoom)
188
- fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="plotly")
210
+ plotter = LocusZoomPlotter(species="canine", backend="plotly")
211
+ fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
189
212
  fig.write_html("plot.html")
190
213
 
191
214
  # Interactive Bokeh (dashboard-ready)
192
- fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="bokeh")
215
+ plotter = LocusZoomPlotter(species="canine", backend="bokeh")
216
+ fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
193
217
  ```
194
218
 
195
219
  | Backend | Output | Best For | Features |
196
220
  |---------|--------|----------|----------|
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 |
221
+ | `matplotlib` | Static PNG/PDF/SVG | Publication-ready figures | Full feature set with SNP labels |
222
+ | `plotly` | Interactive HTML | Web reports, exploration | Hover tooltips, pan/zoom |
223
+ | `bokeh` | Interactive HTML | Dashboard integration | Hover tooltips, pan/zoom |
200
224
 
201
225
  > **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.
202
226
 
@@ -215,7 +239,8 @@ fig = plotter.plot_stacked(
215
239
  )
216
240
  ```
217
241
 
218
- ![Example stacked plot](examples/stacked_plot.png)
242
+ ![Example stacked plot comparing two phenotypes](examples/stacked_plot.png)
243
+ *Stacked plot comparing two phenotypes with LD coloring and shared gene track.*
219
244
 
220
245
  ## eQTL Overlay
221
246
 
@@ -238,6 +263,7 @@ fig = plotter.plot_stacked(
238
263
  ```
239
264
 
240
265
  ![Example eQTL overlay plot](examples/eqtl_overlay.png)
266
+ *eQTL overlay with effect direction (up/down triangles) and magnitude binning.*
241
267
 
242
268
  ## Fine-mapping Visualization
243
269
 
@@ -260,19 +286,62 @@ fig = plotter.plot_stacked(
260
286
  ```
261
287
 
262
288
  ![Example fine-mapping plot](examples/finemapping_plot.png)
289
+ *Fine-mapping visualization with PIP line and credible set coloring (CS1/CS2).*
290
+
291
+ ## PheWAS Plots
292
+
293
+ Visualize associations of a single variant across multiple phenotypes:
294
+
295
+ ```python
296
+ phewas_df = pd.DataFrame({
297
+ "phenotype": ["Height", "BMI", "T2D", "CAD", "HDL"],
298
+ "p_value": [1e-15, 0.05, 1e-8, 1e-3, 1e-10],
299
+ "category": ["Anthropometric", "Anthropometric", "Metabolic", "Cardiovascular", "Lipids"],
300
+ })
301
+
302
+ fig = plotter.plot_phewas(
303
+ phewas_df,
304
+ variant_id="rs12345",
305
+ category_col="category",
306
+ )
307
+ ```
308
+
309
+ ![Example PheWAS plot](examples/phewas_plot.png)
310
+ *PheWAS plot showing associations across phenotype categories with significance threshold.*
311
+
312
+ ## Forest Plots
313
+
314
+ Create forest plots for meta-analysis visualization:
315
+
316
+ ```python
317
+ forest_df = pd.DataFrame({
318
+ "study": ["Study A", "Study B", "Study C", "Meta-analysis"],
319
+ "effect": [0.45, 0.52, 0.38, 0.46],
320
+ "ci_lower": [0.30, 0.35, 0.20, 0.40],
321
+ "ci_upper": [0.60, 0.69, 0.56, 0.52],
322
+ "weight": [25, 35, 20, 100],
323
+ })
324
+
325
+ fig = plotter.plot_forest(
326
+ forest_df,
327
+ variant_id="rs12345",
328
+ weight_col="weight",
329
+ )
330
+ ```
331
+
332
+ ![Example forest plot](examples/forest_plot.png)
333
+ *Forest plot with effect sizes, confidence intervals, and weight-proportional markers.*
263
334
 
264
335
  ## PySpark Support
265
336
 
266
- For large-scale genomics data, pass PySpark DataFrames directly:
337
+ For large-scale genomics data, convert PySpark DataFrames with `to_pandas()` before plotting:
267
338
 
268
339
  ```python
269
340
  from pylocuszoom import LocusZoomPlotter, to_pandas
270
341
 
271
- # PySpark DataFrame (automatically converted)
272
- fig = plotter.plot(spark_gwas_df, chrom=1, start=1000000, end=2000000)
273
-
274
- # Or convert manually with sampling for very large data
342
+ # Convert PySpark DataFrame (optionally sampled for very large data)
275
343
  pandas_df = to_pandas(spark_gwas_df, sample_size=100000)
344
+ fig = plotter.plot(pandas_df, chrom=1, start=1000000, end=2000000)
276
345
  ```
277
346
 
278
347
  Install PySpark support: `uv add pylocuszoom[spark]`
@@ -0,0 +1,29 @@
1
+ pylocuszoom/__init__.py,sha256=UtrNrjV0b0frxv3Zl4jw5D8aTMbNSE55j-PPkd8rz28,5585
2
+ pylocuszoom/colors.py,sha256=B28rfhWwGZ-e6Q-F43iXxC6NZpjUo0yWk4S_-vp9ZvU,7686
3
+ pylocuszoom/ensembl.py,sha256=q767o86FdcKn4V9aK48ESFwNI7ATlaX5tnwjZReYMEw,14436
4
+ pylocuszoom/eqtl.py,sha256=OrpWbFMR1wKMCmfQiC-2sqYx-99TT2i1cStIrPWIUOs,5948
5
+ pylocuszoom/finemapping.py,sha256=ZPcnc9E6N41Su8222wCqBkB3bhhyfASvj9u9Ot4td4o,5898
6
+ pylocuszoom/forest.py,sha256=302gULz9I0UiwqgcB18R756OOl1aa54OsPYHc6TnxGY,1092
7
+ pylocuszoom/gene_track.py,sha256=PkBwfqByVxhXlAPco9-d4P5X7cTg2rrOnw7BJVx48ow,17818
8
+ pylocuszoom/labels.py,sha256=Ams5WVZFNVT692BRiQ5wZcdbdNEAm5xtgRwmF5u0s_A,3492
9
+ pylocuszoom/ld.py,sha256=64xIulpDVvbMSryWUPoCQ99Odcjwf1wejpwVr_30MLU,6412
10
+ pylocuszoom/loaders.py,sha256=KpWPBO0BCb2yrGTtgdiOqOuhx2YLmjK_ywmpr3onnx8,25156
11
+ pylocuszoom/logging.py,sha256=nZHEkbnjp8zoyWj_S-Hy9UQvUYLoMoxyiOWRozBT2dg,4987
12
+ pylocuszoom/phewas.py,sha256=6g2LmwA5kmxYlHgPxJvuXIMerEqfqgsrth110Y3CgVU,968
13
+ pylocuszoom/plotter.py,sha256=gFywhaHPuXlbKPxWaWfw7Wrw8kqPMUPzKMgDcRB6wu8,50709
14
+ pylocuszoom/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ pylocuszoom/recombination.py,sha256=97GGBLDLTlQSRMp5sLOna3mCeRxeJiiWPHrw4dBRjQs,14546
16
+ pylocuszoom/schemas.py,sha256=vABBBlAR1vUP6BIewZ8E-TYpacccrcxavrdIDVCrQB4,11916
17
+ pylocuszoom/utils.py,sha256=_rI6ov0MbsWlZGJ7ni-V4387cirmJCX6IF2JAYhBx6A,6929
18
+ pylocuszoom/validation.py,sha256=UInqlhOWhWaCT_mrO7O7SfB1DNIYkjvEMudy4YjtUBg,5698
19
+ pylocuszoom/backends/__init__.py,sha256=xefVj3jVxmYwVLLY5AZtFqTPMehQxZ2qGd-Pk7_V_Bk,4267
20
+ pylocuszoom/backends/base.py,sha256=PBdm9t4f_qFDMkYR5z3edW4DvpuQSCAXuaxs2qjAeH0,21034
21
+ pylocuszoom/backends/bokeh_backend.py,sha256=11zRhXH2guUHiaYXyd7l2IDAv6uawdRAv6dyGPkHmJc,25512
22
+ pylocuszoom/backends/hover.py,sha256=Hjm_jcxJL8dDxO_Ye7jeWAUcHKlbH6oO8ZfGJ2MzIFM,6564
23
+ pylocuszoom/backends/matplotlib_backend.py,sha256=098ITnvNrBTaEztqez_7D0sZ_rKAYIxS6EDR5Yxt8is,20924
24
+ pylocuszoom/backends/plotly_backend.py,sha256=A6ZuHw0wVZaIIA6FgYJ4SH-Sz59tHOtnGUl-e-2VzZM,30574
25
+ pylocuszoom/reference_data/__init__.py,sha256=qqHqAUt1jebGlCN3CjqW3Z-_coHVNo5K3a3bb9o83hA,109
26
+ pylocuszoom-0.8.0.dist-info/METADATA,sha256=VqHRvFL1Wq5OJO3B727Rl0H8UfbBPaxVIJUOSA22s5A,17866
27
+ pylocuszoom-0.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
28
+ pylocuszoom-0.8.0.dist-info/licenses/LICENSE.md,sha256=U2y_hv8RcN5lECA3uK88irU3ODUE1TDAPictcmnP0Q4,698
29
+ pylocuszoom-0.8.0.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- pylocuszoom/__init__.py,sha256=kEfcTSdVSQgP85IdHDqCQ-oEdq_-8n_Rg-xWWtHzKYk,4806
2
- pylocuszoom/colors.py,sha256=IyzB6x5Q3kkulv-AnYoFVgvibgGgQYE27XjPx99BI5E,6624
3
- pylocuszoom/eqtl.py,sha256=9lZJ8jT1WEj3won6D9B54xdqUvbRvxpOitf97NCUR28,6167
4
- pylocuszoom/finemapping.py,sha256=PJ4HJYeCaHZecUmADCEGQxKd9HhhjrdIA1H5LQsUmLI,6332
5
- pylocuszoom/gene_track.py,sha256=VWvPY0SrVFGJprTdttJ72r3JD-r3bdRDr0HDBai0oJw,18692
6
- pylocuszoom/labels.py,sha256=Ams5WVZFNVT692BRiQ5wZcdbdNEAm5xtgRwmF5u0s_A,3492
7
- pylocuszoom/ld.py,sha256=64xIulpDVvbMSryWUPoCQ99Odcjwf1wejpwVr_30MLU,6412
8
- pylocuszoom/loaders.py,sha256=MK0jUpb09CLMuQYzIY2P1FF3hhtTwemLSiWv4RvLVf8,24350
9
- pylocuszoom/logging.py,sha256=nZHEkbnjp8zoyWj_S-Hy9UQvUYLoMoxyiOWRozBT2dg,4987
10
- pylocuszoom/plotter.py,sha256=A7phON4VYrzFZM0CjSlWwMPLYJmjGV1JF1uKHD8Ml2A,44205
11
- pylocuszoom/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- pylocuszoom/recombination.py,sha256=_W6YVO8a8G8UmGGVda8J_MRI9dOJnffKj8491ILQf3Y,13807
13
- pylocuszoom/schemas.py,sha256=LRUrtgSYH8CZ7G14VSvSL_Z-p4EQBSv2r5WzyKnROh8,11454
14
- pylocuszoom/utils.py,sha256=fKNX9WSTbfHR1EpPYijt6ycNjXEjwzunQMHXAvHaK3s,5211
15
- pylocuszoom/backends/__init__.py,sha256=7dlGvDoqMVK3fCtoMcI9zOP9qO0odQGPwfXhxnLfXfI,1196
16
- pylocuszoom/backends/base.py,sha256=yLZkr5FRlYHs8L9ViNbTwu8hrBaHoVv_QbMujad2aTc,9793
17
- pylocuszoom/backends/bokeh_backend.py,sha256=OFx_FISiDFG-A6NXcR8V-2MgkTRq2dXEcpqaWxf0YUg,21528
18
- pylocuszoom/backends/matplotlib_backend.py,sha256=dUgH3ouQCkh55aufvjNIvkEqMG9oamKKvQYp2AEm4DY,11479
19
- pylocuszoom/backends/plotly_backend.py,sha256=U3odXYLVCwTC6Xb-NeOs456tlr_qJQxgix7QIjJX-3Q,26922
20
- pylocuszoom/reference_data/__init__.py,sha256=qqHqAUt1jebGlCN3CjqW3Z-_coHVNo5K3a3bb9o83hA,109
21
- pylocuszoom-0.5.0.dist-info/METADATA,sha256=_0mgXIY3m1x5ATnQpLfAdRvBjh8iwW_FnX4i-aX4ne8,15228
22
- pylocuszoom-0.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
23
- pylocuszoom-0.5.0.dist-info/licenses/LICENSE.md,sha256=U2y_hv8RcN5lECA3uK88irU3ODUE1TDAPictcmnP0Q4,698
24
- pylocuszoom-0.5.0.dist-info/RECORD,,